24
FACULDADE ANHANGUERA DE CAMPINAS – UNIDADE 2 CIÊNCIA DA COMPUTAÇÃO ESTRUTURAS DE DADOS AULA 2 REVISÃO DE LINGUAGEM C 1 - ESTRUTURAS PRIMITIVAS (TIPOS DE DADOS) São os tipos de dados ou elementos primitivos que sobre os quais são construídas outras mais complexas. A seguir estão os tipos básicos implementados na maioria das linguagens de programação. • Inteiro ou integer que são os tipos de dados do conjunto de inteiros representados pelos números naturais 1, 2, 3, ... , n+1. Sobre esta classe de dados podem ser realizadas as operações (+ , - , / , * , mod). O mod e o resto de uma divisão. • Real (float, double) - São os números do conjunto dos reais ou fracionários representados por 0.1, 1.2, 3.14, 12.00. Sobre esta classe de dados podemos realizar as seguintes operações e resultando valores fracionários (+ , - , * , / ). • Logical (boolean) - São tipos de dados que assumem somente dois valores lógicos (V, F), Verdadeiro ou Falso. Sobre esta classe de dados podemos realizar somente as operações lógicas que são (e, ou, não). • Caracter (char) - São tipos de dados que representam valores literais que pode ser uma letra ou um número ou um símbolo qualquer. Esta classe de dados suporta operações de concatenação, substring, tamanho. Para armazenar valores inteiros, a linguagem C oferece alguns tipos básicos, tais como: char, short int, long int. Estes tipos diferem entre si pelo espaço de memória utilizado e consequentemente pelo intervalo de valores que podem representar. O tipo char, por exemplo, ocupa 1 byte, podendo representar 2 8 = 256 valores distintos. Os tipos short int e long int podem ser referenciados simplesmente com short e long, respectivamente. O tipo int é transformado para o tipo inteiro natural da máquina. O tipo char é um literal e é usado para representar códigos de caracteres, podendo ser convertido para inteiro se for um literal de 0 a 9. Para mais detalhes sobre conversão (cast) de tipos de dados, consultar a bibliografia sobre linguagem C. Abaixo apresentamos uma tabela contendo os detalhes sobre os tipos primitivos usados na linguagem C. O tipo boolean ou lógico não existe na linguagem C. Esta linguagem utiliza os valores inteiros 0 para falso e qualquer outro valor para verdadeiro. Tabela 1.1 - Faixa de valores para os tipos primitivos Nome tamanho em bits faixa de valores char 8 -128 a 127 int 16 -32.768 a 32.767 fIoat 32 10 -38 a 10 38 long int 32 -2.147.483.648 a 2.147.483.647 double 64 10 -308 a 10 308 Página 1

02 Revisao C

Embed Size (px)

DESCRIPTION

Estruturas de DaDOS

Citation preview

Page 1: 02 Revisao C

FACULDADE ANHANGUERA DE CAMPINAS – UNIDADE 2CIÊNCIA DA COMPUTAÇÃO

ESTRUTURAS DE DADOS

AULA 2

REVISÃO DE LINGUAGEM C

1 - ESTRUTURAS PRIMITIVAS (TIPOS DE DADOS)

São os tipos de dados ou elementos primitivos que sobre os quais são construídas outrasmais complexas. A seguir estão os tipos básicos implementados na maioria das linguagens deprogramação.

• Inteiro ou integer que são os tipos de dados do conjunto de inteiros representados pelosnúmeros naturais 1, 2, 3, ... , n+1. Sobre esta classe de dados podem ser realizadas as operações(+ , - , / , * , mod). O mod e o resto de uma divisão.

• Real (float, double) - São os números do conjunto dos reais ou fracionários representadospor 0.1, 1.2, 3.14, 12.00. Sobre esta classe de dados podemos realizar as seguintes operações eresultando valores fracionários (+ , - , * , / ).

• Logical (boolean) - São tipos de dados que assumem somente dois valores lógicos (V, F),Verdadeiro ou Falso. Sobre esta classe de dados podemos realizar somente as operações lógicasque são (e, ou, não).

• Caracter (char) - São tipos de dados que representam valores literais que pode ser umaletra ou um número ou um símbolo qualquer. Esta classe de dados suporta operações deconcatenação, substring, tamanho.

Para armazenar valores inteiros, a linguagem C oferece alguns tipos básicos, tais como:char, short int, long int. Estes tipos diferem entre si pelo espaço de memória utilizado econsequentemente pelo intervalo de valores que podem representar.

O tipo char, por exemplo, ocupa 1 byte, podendo representar 28 = 256 valores distintos.

Os tipos short int e long int podem ser referenciados simplesmente com short e long,respectivamente. O tipo int é transformado para o tipo inteiro natural da máquina.

O tipo char é um literal e é usado para representar códigos de caracteres, podendo serconvertido para inteiro se for um literal de 0 a 9. Para mais detalhes sobre conversão (cast) detipos de dados, consultar a bibliografia sobre linguagem C.

Abaixo apresentamos uma tabela contendo os detalhes sobre os tipos primitivos usados nalinguagem C. O tipo boolean ou lógico não existe na linguagem C. Esta linguagem utiliza osvalores inteiros 0 para falso e qualquer outro valor para verdadeiro.

Tabela 1.1 - Faixa de valores para os tipos primitivos

Nome tamanho em bits faixa de valores

char 8 -128 a 127

int 16 -32.768 a 32.767

fIoat 32 10-38 a 1038

long int 32 -2.147.483.648 a 2.147.483.647

double 64 10-308 a 10308

Página 1

Page 2: 02 Revisao C

2 - VARIÁVEIS

Variáveis são identificadores de dados que podem assumir vários valores, como o próprionome já diz. A cada momento de execução de um algoritmo, uma variável pode estar com umvalor diferente.

Uma variável representa um espaço na memória do computador para armazenar um dadoqualquer. Na linguagem C, todas as variáveis devem ser explicitamente declaradas. Nadeclaração de uma variável, obrigatoriamente, devem ser especificados seu tipo e seu nome. Onome da variável serve de referência (endereçamento relativo) ao dado armazenado no espaçode memória e o tipo do dado determina a natureza do dado que será armazenado.

Ao utilizarmos uma linguagem como C, devemos declarar variáveis seguindo a sintaxe destalinguagem. A declaração de uma variável reserva um espaço na memória para armazenar umdado do tipo da variável e associa o nome da variável a este espaço de memória.

Por exemplo, nas declarações abaixo, a e b são variáveis do tipo inteiro e x é uma variáveldo tipo float.

int a;

int b; float x;

Atribuições de valores às variáveis podem ser diretamente ou por meio de cálculo ou leituracomo mostraremos logo a seguir.

a=5;

b=4;

x = 4.5;

ou ainda;

a = b + 1;

A linguagem C permite que variáveis de mesmo. tipo sejam declaradas juntas. Assim, asduas primeiras declarações acima poderiam ser substituídas por:

int a, b; /* -- declara duas variáveis do tipo int -- */

Uma vez declarada a variável, podemos armazenar valores nos respectivos espaços dememória. Estes valores devem ter o mesmo tipo da variável, conforme ilustrado acima. Em C, asvariáveis podem ser inicializadas na decLaração. Podemos, por exemplo, escrever:

int a;

a=4;

/* -- a variável armazenará o valor 4 ----*/

int a = 5, b = 10; /* declara e inicializa as variáveis a e b */

float c = 5.3; /* -- atribui o valor 5.3 a variável c --*/

Podemos usar também valores constantes. Quando escrevemos a atribuição seguintea=b+123, sendo a e b variáveis supostamente já declaradas, reserva-se um espaço para aarmazenar a constante 123. No caso, a constante é do tipo inteiro, então um espaço de dois bytes(em geral) é reservado e o valor 123 é armazenado nele.

A diferença básica em relação às variáveis, como os nomes dizem (variáveis e constantes),é que o valor armazenado numa área de constante não pode ser alterado.

As constantes também podem ser do tipo real. Uma constante real deve ser escrita com umponto decimal ou valor de expoente. Sem nenhum sufixo, uma constante real é do tipo double. Se

Página 2

Page 3: 02 Revisao C

quisermos uma constante real do tipo float, devemos, a rigor, acrescentar o sufixo F ou f. Algunsexemplos de constantes reais são:

12.45 - real do tipo double;

1245e-2 - Real do tipo double;

12.45F - real do tipo float;

Alguns compiladores exibem uma advertência quando encontram o trecho de códigoseguinte.

float x;

x = 12.45;

O código, a rigor, armazena um valor double (12.45) numa variável do tipo float.

Desde que a constante seja representável dentro de um float, não precisamos nospreocupar com este tipo de advertência.

Um dos erros comuns em programas de computador é o uso de variáveis cujos valoresainda estão indefinidos. Por exemplo, o trecho de código abaixo está errado, pois o valorarmazenado na variável b está indefinido e tentamos usá-Io na atribuição de c. Assim, a variável btem "lixo" ou resquício de dados na memória, pois a linguagem C não inicializa variáveis ao seremdeclaradas.

int a, b, c;

a=2;

c = a + b; /* ERRO: b tem "lixo" */

Alguns destes erros são óbvios e o compilador é capaz de nos reportar uma advertência. Noentanto, muitas vezes o uso de uma variável não definida fica difícil de ser identificado no código.Repetimos que é um erro comum em programas e uma razão para alguns programas funcionaremna parte da manhã e não funcionarem na parte da tarde (ou funcionarem durante odesenvolvimento e não funcionarem quando entram em produção). Poder funcionar uma vez enão funcionar outra é que, apesar de indefinido, o valor da variável existe. No nosso caso acima,pode acontecer que o valor armazenado na memória ocupada por b seja 0, fazendo com que oprograma funcione. Pode acontecer da variável ter o valor -354332 e o programa não funcionar.

3 - OPERADORES

Os operadores são instruções que realizam transformações e comparações dos dados ouoperandos e podem ser divididos em três grupos como descrito abaixo: A linguagem C ofereceuma gama variada de operadores, entre binários e unários. Os operadores básicos sãoapresentados a seguir.

3.1 - Operadores Aritméticos e Associatividades

Os operadores aritméticos binários são: +, -, * , / e o operador módulo %. A operação é feitana precisão dos operandos. Assim, a expressão 5/2 resulta no valor 2, pois a operação de divisãoé feita em precisão inteira, já que os dois operandos (5 e 2) são constantes inteiras. A divisão deinteiros trunca a parte fracionária, pois o valor resultante é sempre do mesmo tipo da expressão.Consequentemente, a expressão 5.0 / 2.0 resulta no valor real 2.5, pois a operação é feita naprecisão real (double, no caso).

O operador módulo, %, não se aplica a valores reais, seus operandos devem ser do tipointeiro. Este operador produz o resto da divisão do primeiro pelo segundo operando. Comoexemplo de aplicação deste operador, podemos citar o caso em que desejamos saber se o valorarmazenado numa determinada variável inteira x é par ou ímpar. Para tanto, basta analisar o

Página 3

Page 4: 02 Revisao C

resultado da aplicação do operador %, aplicado à variável e ao valor dois, x % 2, se resultado forzero.

Um número é dito par se (x % 2) for igual a zero e ímpar, caso contrário. Os operadores *, /e % têm precedência maior que os operadores + e -.

Operadores com mesma precedência são avaliados da esquerda para a direita.

Assim, na expressão: a + b * c / d executa-se primeiro a multiplicação, seguida da divisão,seguida da soma. Podemos utilizar parênteses para alterar a ordem de avaliação de umaexpressão. Assim, se quisermos avaliar a soma primeiro, podemos escrever: (a + b) * c / d.

A linguagem C também permite utilizar os chamados operadores de atribuição compostos.Comandos do tipo: i = i + 2; em que a variável à esquerda do sinal de atribuição também apareceà direita, podem ser escritas de forma mais compacta: i += 2; usando o operador de atribuiçãocomposto +=. Analogamente, existem, entre outros, os operadores de atribuição: -=, *=, /=, %=.De forma geral, comandos do tipo: var op = expr; são equivalentes a: var = var op (expr);Salientamos a presença dos parênteses em torno de expr. Assim: x *= y + 1; equivale a x=x*(y+1)e não a x = x * y + 1.

Há dois outros operadores não convencionais, que são os de incremento e decremento queservem para incrementar e decrementar uma unidade nos valores armazenados nas variáveis.Assim, se n é uma variável que armazena um valor, o comando: n++; incrementa em uma unidadeo valor de n. O aspecto não usual é que ++ e -- podem ser usados tanto como operadoresprefixados (antes da variável, como em ++n) ou pós-fixados (após a variável, como em n++). Emambos os casos, a variável n é incrementada. Porém, a expressão ++n incrementa n antes deusar seu valor, enquanto n++ incrementa n após seu valor ser usado. Isto significa que, numcontexto onde o valor de n é usado, ++n e n++ são diferentes. Se n armazena o valor 5, então: x =n++; atribui 5 a x, mas: x = ++n; atribuiria 6 a x. Em ambos os casos, n passa a valer 6, pois seuvalor foi incrementado em uma unidade.

Os operadores de incremento e decremento podem ser aplicados somente em variáveis.Uma expressão do tipo x = (i + 1)++ é incorreta. A linguagem C oferece diversas formascompactas para escrevermos um determinado comando.

Mesmo para programadores experientes, o uso das formas compactas deve ser feito comcritério. Por exemplo, os comandos: a = a + 1; a += 1; a++; ++a; são todos equivalentes e oprogramador deve escolher o que achar mais adequado e simples.

3.2 - Operadores Relacionais

Os operadores relacionais são aqueles que estabelecem uma relação entre dois operandosou expressões. Na linguagem em C são os seguintes.

< menor que;

> maior que;

<= menor ou igual que;

>= maior ou igual que;

== igual a;

!= diferente de.

Estes operadores comparam dois valores. O resultado produzido por um operador relacionalé 0 ou 1. Em C, não existe o tipo booleano (true ou false). O valor zero é interpretado como falso equalquer valor diferente de zero é considerado verdadeiro. Assim, se o resultado de umacomparação for falso, produz-se o valor 0, caso contrário, produz-se o valor 1.

Página 4

Page 5: 02 Revisao C

3.3 - Operadores Lógicos

Os operadores lógicos combinam expressões booleanas. A linguagem oferece os seguintesoperadores lógicos:

&& operador binário E (AND);

|| operador binário OU (OR);

! operador unário de NEGAÇÃO (NOT).

Expressões conectadas por && ou || são avaliadas da esquerda para a direita, e a avaliaçãopára assim que a veracidade ou falsidade do resultado for conhecida. Recomendamos o uso deparênteses em expressões que combinam operadores lógicos e relacionais. Os operadoresrelacionais e lógicos são normalmente utilizados para tomada de decisões. No entanto, podemosutilizá-Ios para atribuir valores a variáveis. Por exemplo, o trecho de código abaixo é válido earmazena o valor 1 em a e 0 em b.

int a, b;

int c=23;

int d = c + 4;

a = (c < 20) || (d > c); /* verdadeiro */

b = (c < 20) && (d > c); /* falso */

Devemos salientar que, na avaliação da expressão atribuída à variável b, a operação (d>c)não chega a ser avaliada, pois independente do seu resultado a expressão como um todo terácomo resultado 0 (falso), uma vez que a operação (c<20) tem valor falso.

3.4 - Operador sizeof

Outro operador fornecido por C, sizeof, resulta no número de bytes de um determinado tipo.Por exemplo, int a = sizeof(float). Armazena o valor 4 na variável a, pois um float ocupa 4 bytes dememória. Este operador pode também ser aplicado a uma variável, retomando o número de bytesdo tipo associado à variável.

3.5 - Conversão de Tipo

Existem conversões automáticas de valores na avaliação de uma expressão. Assim, naexpressão 3/1.5, o valor da constante 3 (tipo int) é promovido (convertido) para double antes de aexpressão ser avaliada, pois o segundo operando é do tipo double (1.5) e a operação é feita naprecisão do tipo mais representativo.

Quando, numa atribuição, o tipo do valor atribuído é diferente do tipo da variável, também háuma conversão automática de tipo. Por exemplo, se escrevermos int a = 3.5; o valor 3.5 éconvertido para inteiro (isto é, passa a valer 3) antes de a atribuição ser efetuada. Comoresultado, o valor atribuído à variável é 3 (inteiro). Alguns compiladores exibem advertênciasquando a conversão de tipo pode significar uma perda de precisão (é o caso da conversão realpara inteiro, por exemplo).

O programador pode explicitamente requisitar uma conversão de tipo através do uso dooperador de molde de tipo (operador cast). Por exemplo, são válidos os comandos abaixo.

int a, b; // declara duas variáveis inteiras

char c; // declara uma variável do tipo caractere.

C = 5; // atribuição de um caractere 5 para c.

a = (int) 3.5; // a variável a recebe o valor 3 convertido parainteiro

Página 5

Page 6: 02 Revisao C

b = (int) 3.5 % 2; // a variável b recebe um valor inteiro 1.

b = c + a; // a variável b ficará com o valor 8 que é 3 + 5.

4 - ENTRADAS E SAÍDAS

As operações de entrada e saída são realizadas por funções. Existe em C uma bibliotecapadrão que possui as funções básicas necessárias. Na biblioteca padrão, podemos encontrarfunções matemáticas do tipo raiz quadrada, seno, cosseno etc., funções para a manipulação decadeias de caracteres e funções de entrada e saída.

Nesta seção, serão apresentadas as duas funções básicas de entrada e saídadisponibilizadas pela biblioteca padrão. É necessário escrever: #include <stdio.h> no início doprograma que utiliza as funções da biblioteca de entrada e saída.

4.1 - Função printf

A função printf possibilita a saída de valores (sejam eles constantes, variáveis ou resultadode expressões), segundo um determinado formato. Informalmente, podemos dizer que a forma dafunção é: printf (formato, lista de constantes/variáveis/expressões ... ).

O primeiro parâmetro é uma cadeia de caracteres, em geral delimitada com aspas, queespecifica o formato de saída das constantes, variáveis e expressões listadas em seguida.

Para cada valor que se deseja imprimir, deve existir um especificador de formatocorrespondente na cadeia de caracteres formato. Os especificadores de formato variam com otipo do valor e a precisão em que queremos que eles sejam impressos. Estes especificadores sãoprecedidos pelo caractere % e especificam os seguintes tipos:

%c - char;

%d - int;

%i - int;

%u - unsigned int;

%f - double (ou float);

%e - double (ou float) no formato científico;

%g - double (ou float) no formato mais apropriado (%f ou %e);

%s - cadeia de caracteres.

Com base nesses conceitos, vamos apresentar alguns exemplos como segue. A funçãoprintf ("%d %g\n", 33, 5.3) tem como resultado a impressão da linha: 33 5.3, ou ainda: printf("Inteiro = %d Real = %g\n", 33, 5.3), com saída: Inteiro = 33 Real = 5.3. Isto é, além dosespecificadores de formato, podemos incluir textos no formato, que são mapeados diretamentepara a saída.

Assim, a saída é formada pela cadeia de caracteres do formato onde os especificadores sãosubstituídos pelos valores correspondentes. Existem alguns caracteres de escape que sãofrequentemente utilizados nos formatos de saída. São eles:

\n - nova linha;

\t - tabulação;

\r - de retrocesso;

\" o caractere ";

\\ o caractere \.

Página 6

Page 7: 02 Revisao C

%% o caracter %

É possível também especificar o tamanho dos campos, como segue:

%4d;

%7.2f;

A função printf retorna o número de campos impressos. Salientamos que para cadaconstante, variável ou expressão listada devemos ter um especificador de formato apropriado.

4.2 - Função scanf

A função scanf permite capturarmos valores fornecidos via teclado pelo usuário doprograma. Informalmente, podemos dizer que sua forma geral é: scanf (formato, lista deendereços das variáveis). O formato deve possuir especificadores de tipos similares aosmostrados para a função printf. Para a função scanf, no entanto, existem especificadoresdiferentes para o tipo float e double como segue:

%c – char;

%d - int;

%u - unsigned int;

%f,%e,%g - float;

%If, %Ie, %Ig - double;

%s - cadeia de caracteres.

A principal diferença é que o formato deve ser seguido por uma lista de endereços devariáveis (na função printf passamos os valores de constantes, variáveis e expressões). Por ora,basta saber que, para ler um valor e atribuí-Io a uma variável, devemos passar o endereço davariável para a função scanf. O operador & retoma o endereço de uma variável. Assim, para lerum inteiro, devemos ter:

int n;

scanf("%d", &n);

Para a função scanf, os especificadores %f, %e e %g são equivalentes. Aqui, caracteresdiferentes dos especificadores no formato servem para restringir a entrada. Por exemplo:scanf("%d:%d", &h, &m); obriga que os valores (inteiros) fornecidos sejam separados pelocaractere dois pontos (:).

Um espaço em branco dentro do formato faz com que sejam "pulados" eventuais brancos daentrada. Os especificadores %d, %f, %e e %g automaticamente pulam os brancos queprecederem os valores numéricos a serem capturados. A função scanf retorna o número decampos lidos com sucesso.

5 - CONTROLE DE FLUXO

5.1 - Decisão ou Seleção com if

O comando de seleção ou decisão if é um comando de decisão básico. A forma de se usareste comando está exemplificado a seguir, onde temos exemplos de seleção simples e composta.

if (expr) {

bloco de comandos 1

}

Página 7

Page 8: 02 Revisao C

ou

if(expr){

bloco de comandos 1

}else{

bloco de comandos 2

}

Se expr produzir um valor diferente de 0 (verdadeiro), o bloco de comandos 1 seráexecutado. A inclusão do else requisita a execução do bloco de comandos 2, se a expressãoproduzir o valor 0 (falso). Cada bloco de comandos deve ser delimitado por uma chave aberta euma chave fechada. Se dentro de um bloco tivermos apenas um comando a ser executado, aschaves podem ser omitidas, como vemos abaixo. Na verdade, deixamos de ter um bloco.

if ( expr )

comandol;

eles

comando2;

A indentação (recuo de linha) dos comandos é fundamental para uma maior clareza docódigo. O estilo de indentação varia a gosto do programador. Podemos aninhar comandos if. Umexemplo simples é ilustrado a seguir:

#include <stdio.h>

void main (void)

{

int a, b;

printf ("Digite dois números inteiros: ");

scanf("%d%d",&a,&b);

if (a%2 == 0)

if (b%2 == 0)

printf ("VoceDigitou dois numeros pares! \n");

}

Primeiramente, notamos que não foi necessário criar blocos ( { ... } ) porque a cada if estáassociado apenas um comando. Ao primeiro, associamos o segundo comando if, e ao segundo if,o comando que chama a função printf. Assim, o segundo if só será avaliado se o primeiro valorfornecido for par, e a mensagem só será impressa se o segundo valor fornecido também for par.Outra construção para o mesmo exemplo produzirá resultados idênticos como escrito a seguir.

void main (void)

{

int a, b;

printf ("Digite dois numeros inteiros: ");

Página 8

Page 9: 02 Revisao C

scanf("%d%d",&a,&b);

if ((a%2 == 0) && (b%2 == 0))

printf ( "Voce digitou dois numeros pares! \n" );

}

Devemos ter cuidado com o aninhamento de comandos if-else. A indentação utilizada podenos levar a erros de interpretação. Para os casos em que a associação entre ife else não estáclara, recomendamos a criação explícita de blocos, mesmo contendo um único comando.Reescrevendo o programa, podemos obter o efeito desejado.

/* temperatura * /

#include <stdio. h>

void main (void)

{

int temp;

printf ("Digite a temperatura: ");

scanf ("%d", &temp);

if (temp < 30)

{

if (temp > 20)

printf ("Temperatura agradavel \n");

}else{

printf ("Temperatura muito quente \n");

}

}

Esta regra de associação do else propicia a construção do tipo else-if, sem que se tenha ocomando elseif explicitamente na gramática da linguagem. Na verdade, em C, construímosestruturas else-if com if's aninhados. O exemplo a seguir é válido e funciona como esperado.

/* - temperatura (versao 1) -- */

#include <stdio. h>

void main (void)

{

int temp;

printf ("Digite a temperatura: ");

scanf ("%d", &temp);

if (temp < 10)

printf ("Temperatura muito fria \n");

else if (temp < 20)

printf (" Temperatura fria \n");

Página 9

Page 10: 02 Revisao C

else if (temp < 30)

printf ("Temperatura agradavel \n");

else

printf ("Temperatura muito quente \n");

5.2 - Estruturas de Bloco

Uma função é composta por estruturas de blocos. Cada chave aberta e fechada em Crepresenta um bloco. As declarações de variáveis só podem ocorrer no início do corpo da funçãoou no início de um bloco, isto é, devem seguir uma chave aberta. Uma variável declarada dentrode um bloco é válida apenas dentro do bloco ou variável local. Após o término do bloco, a variáveldeixa de existir. Por exemplo:

...

if(n>0)

{

int i;

...

}

... /* a variável i não existe neste ponto do programa */

A variável i, definida dentro do bloco do if, só existe dentro deste bloco. É uma boa práticade programação declarar as varáveis o mais próximo possível dos seus usos.

5.3 - Construções com Laços

A iteratividade é um dos recursos mais poderosos em programas computacionais.

São procedimentos iterativos, isto é, procedimentos que devem ser executados em váriospassos.

Para calcular o fatorial de um número através de um programa de computador, utilizamostipicamente um processo iterativo, em que o valor da variável varia de 1 a n. A linguagem Coferece diversas construções possíveis para a realização de laços iterativos. O primeiro a serapresentado é o comando while. Sua forma geral é:

while (expr)

{

bloco de comandos

}

Enquanto expr for avaliada em verdadeiro, o bloco de comandos é executadorepetidamente. Se expr for avaliada como falso, o bloco de comando não é executado e aexecução do programa prossegue após o final do bloco do laço. Uma possível implementação docálculo do fatorial usando while é mostrada a seguir.

/* -- fatorial --- */

#include <stdio.h>

void main (void)

Página 10

Page 11: 02 Revisao C

{

int I, n;

int f = 1;

printf ("Digite um número inteiro:");

scanf ("%d", &n);

/* --- calcula fatorial --- */

i = 1;

whi1e (i <= n){

f *= i;

i++;

}

printf("Fatorial %d \n", f);

}

Uma segunda forma de construção de laços, mais compacta e amplamente utilizada, éatravés de laços for. A forma geral do for é:

for (expr_inicial; expr_relacional; expr_de_incremento)

{

bloco de comandos

...

}

A implementação do laço for pode ser verificada no código a seguir, onde ilustramos autilização do comando for no programa para cálculo do fatoria!'

/* -- fatorial ---) */

#include <stdio .h>

void main (void)

{

int I, int n;

int f = 1;

printf ("Digite um número inteiro:");

scanf("%d", &n);

/* calcula fatorial */

for (i = 1; i <= n; i++){

f *= i;

}

printf(" Fatorial %d \n", f);

}

Página 11

Page 12: 02 Revisao C

Observamos que as chaves que seguem o comando for, neste caso, são desnecessárias, jáque o corpo do bloco é composto por um único comando. Tanto a construção com while como aconstrução com for avaliam a expressão booleana que caracteriza o teste de encerramento noinício do laço. Assim, se esta expressão tiver valor igual a zero (falso), quando for avaliada pelaprimeira vez, os comandos do corpo do bloco não serão executados nenhuma vez.

Há um outro comando para construção de laços cujo teste de encerramento é avaliado nofinal. Esta construção é o do-while, cuja forma geral é:

do{

bloco de comandos

} while (expr_booleana);

Um exemplo do uso desta construção é mostrado abaixo, onde validamos a inserção dousuário, isto é, o programa repetidamente solicita a inserção de um número enquanto o usuárioinserir um inteiro negativo.

/* f atorial ---- */

#include <stdio.h>

void main (void)

{

int I n;

int f = 1;

do{

printf (“Digite um valor inteiro nao negativo: ”);

scanf (“%d”, &n);

} while (n<0);

/* calcula fatorial */

for (i = 1; i <= n; i++) {

f *= i;

}

printf("Fatorial = %d\n”, f);

}

5.4 Interrupções com break e continue

O comando break, quando utilizado dentro de um laço, interrompe e termina a execução domesmo. A execução prossegue com os comandos subsequentes ao bloco. O código abaixo ilustrao efeito de sua utilização.

#include <stdio. h>

void main (void)

{

int i;

for (i = 0; i < 10; i++)

Página 12

Page 13: 02 Revisao C

if (i == 5)

break;

printf("%d", i);

}

printf("fim\n”);

}

A saída deste programa, se executado, será: 01234 fim pois, quando i tiver o valor 5, o laçoserá interrompido e finalizado pelo comando break, passando o controle para o próximo comandoapós o laço, no caso uma chamada final de printf.

O comando continue também interrompe a execução dos comandos de um laço.

A diferença básica em relação ao comando break é que o laço não é automaticamentefinalizado. O comando continue interrompe a execução de um laço passando para a próximaiteração pulando os comandos existentes, como podemos ver no código abaixo.

#include <stdio.h>

void main (void)

{

int i;

for (i = 0; i < 10; i++)

if (i == 5)

continue;

printf("%d", i);

}

printf("fim\n");

}

O resultado de saída do programa acima é o seguinte conjunto. {0 1 2 3 4 6 7 8 9 fim}.Devemos ter cuidado com a utilização do comando continue nos laços while. Observe umprograma INCORRETO escrito logo abaixo.

/ * - Versão INCORRETA --* /

#include <stdio. h>

void main (void)

{

int i = 0;

while (i < 10){

if (i == 5)

continue;

printf("%d", i);

i++;

Página 13

Page 14: 02 Revisao C

}

printf("fim\n");

}

O programa acima está INCORRETO, pois o laço criado não tem fim. A execução doprograma não termina. Isto porque a variável i nunca terá valor superior a 5, e o teste será sempreverdadeiro. O que ocorre é que o comando continue provoca um salto dos demais comandos dolaço quando i for igual a 5, inclusive o comando que incrementa a variável i.

5.5 - Seleção

Além da construção else-if, temos também o comando (switch) para selecionar um dentreum conjunto de possíveis casos. Sua forma geral está exemplificada abaixo.

int op = 1;

scanf(“%d",&op);

switch (op){

case 1:

.../* comandos executados se op == 1 */

break;

case 2:

.../* comandos executados se op == 2 */

break;

case 3:

.../* comandos executados se op 3 */

break;

default:

.../* executados se op for diferente de todos */

break;

}

A variável op deve ser um número inteiro ou uma constante caractere. Se op resultar novalor op, os comandos que se seguem ao caso op são executados, até que se encontre um break.

Se o comando break for omitido, a execução do caso continua com os comandos do casoseguinte. O caso default (nenhum dos outros) pode aparecer em qualquer posição, masnormalmente é colocado por último.

Para exemplificar, mostramos a seguir um programa que implementa uma calculadoraconvencional que efetua as quatro operações básicas. Este programa usa constantes caracteres.

/* --- Calculadora de quatro operações --- */

#include <stdiooh>

void main (void)

{

float num1, num2; char op;

Página 14

Page 15: 02 Revisao C

printf(“Digite: numero op numero\n");

scanf ("%f%c%f", &numl, &op, &num2);

switch (op){

case '+' :

printf (" = %f\n", numl+num2);

break;

case '-':

printf (" = %f\n", numl-num2);

break;

case '*':

printf (" = %f\n", numl*num2);

break;

case'/' :

printf (" = %f\n", numl/num2);

break;

default:

printf ("Operador invalido! \n");

break;

}

}

6 - PROCEDIMENTOS E FUNÇÓES

São sub-rotinas que implementam uma determinada quantidade de código que realizamuma tarefa bem determinada. São exemplos de funções ou procedimentos: calcular raiz quadrada,obter uma substring, calcular potência etc. Há uma diferença entre procedimento e função.Procedimento ou procedure é uma rotina que não retoma valor e Função é uma rotina que retornum valor determinado. A função só retorn um valor.

Os próximos procedimentos ilustram o uso de rotinas em pseudocódigo e em seguida aimplementação correspondente em linguagem C. O uso do pseudocódigo é livre dos rigores dasintaxe exigida pelo compilador, facilitando o entendimento do problema e a consequente soluçãoatravés da lógica de programação.

A seguir estão alguns exemplos de rotinas que chamam sub-rotinas, tanto em pseudocódigocomo em linguagem C. Algumas rotinas realizam chamadas a outras funções. Observe osexemplos seguintes de pseudocódigos que ilustram o que estamos estudando.

O fragmento de código que segue apresenta um procedimento principal (void) que instanciaalgumas variáveis e realiza as chamadas a uma função obtendo o retomo que será armazenadona variável r e em seguida utiliza outro procedimento imprime(r) para imprimir o valor que foiretorndo.

Após a impressão do resultado da função produto, temos um procedimento denominado desoma que calcula a soma e imprime o resultado. Devemos notar que este último procedimentonão retoma valor algum.

Página 15

Page 16: 02 Revisao C

/* - procedimento principal -- * /

inicio

int x,y,r;

x = 2;

y = 3;

r = produto (x, y); //chamada da função com passagem deparâmetros

imprime(r);

soma (x,y) ;

fim

/* ----Rotina/função produto -----*/

int produto (int k, int z)

inicio

return(k * z);

fim

//---- Procedimento soma ---//

procedimento soma(x, y)

inicio

var x, y: inteiro;

imprime(x + y);

fim

Após o entendimento de como funcionam os procedimentos apresentados anteriormente empseudocódigo, vamos codificá-Ios na linguagem C. Os próximos códigos são implementações emC para os algoritmos anteriores.

/* -.- rotina principal da linguagem C ---*/

void main()

{

int x,y,r;

x=2;

y=3;

r = produto(x, y); //chamada da função com passagem deparâmetros

printf ("%i" ,r);

soma(x, y);

}

Página 16

Page 17: 02 Revisao C

/* ---- função produto ---*/

int produto (int k,int z){

return(k * z);

}

/* -- Procedimento soma ---* /

void soma (x, y){

int x,y;

printf("%d",x+y);

}

A construção de funções tem o objetivo de dividir grandes tarefas de computação em tarefasmenores. A criação de funções evita a repetição de código, de modo que um procedimento que érepetido deve ser transformado numa função que, então, será chamada diversas vezes.

Um programa bem estruturado deve ser planejado em termos de funções, e estas. por suavez, podem esconder do corpo principal do programa detalhes ou particularidades deimplementação. Nesta seção, discutiremos a codificação de nossas próprias funções.

A forma geral para definir uma função pode ser verificada no exemplo a seguir.

tipo_retornado nome_da_função(lista de parâmetros ... ){

corpo da função

}

Para ilustrar a criação de funções, consideraremos o cálculo do fatorial de um número.Podemos escrever uma função que, dado um determinado número inteiro não negativo n, imprimeo valor de seu fatorial. Um programa que utiliza esta função pode ser escrita como segue:

/* --- imprime um fatorial ---* /

#include <stdio.h>

void fat(int n); /* protótipo da função fat() */

/ *-- função principal-- * /

void main (void)

{

int n;

scanf ("%d", &n);

fat(n);

}

/* -- função que imprime o fatorial-- * /

void fat(int n ){

int i;

int f = 1;

Página 17

Page 18: 02 Revisao C

for (i = 1; i <= n; i++)

f *= i;

printf ("Fatorial = %d\n", f);

}

Notamos, neste exemplo, que a função fat recebe como parâmetro o número cujo fatorialdeste deve ser impresso.

Os parâmetros de uma função devem ser listados, com seus respectivos tipos, entre osparênteses que seguem o nome da função.

Devemos notar que main() também é uma função. Sua única particularidade consiste em sera função automaticamente executada após o programa ser carregado.

No exemplo do cálculo do fatorial, a função fat não tem nenhum valor de retorno, portantocolocamos a palavra void antes do nome da função, indicando a ausência de um valor de retorno.

void fat (int n){

...

}

Ressaltamos que C exige que se coloque o protótipo da função antes desta ser chamada,se estas funções forem implementadas após a função main(). O protótipo de uma função consistena repetição da linha de sua definição seguida do caractere (;).

A rigor, no protótipo não há necessidade de indicarmos os nomes dos parâmetros, apenasos seus tipos, portanto seria válido escrever: void fat (int). O protótipo da função é necessário paraque o compilador verifique os tipos dos parâmetros na chamada da função. É devido a estanecessidade que se exige a inclusão do arquivo stdio.h para a utilização das funções de entrada esaída da biblioteca padrão.

Neste arquivo, encontram-se, entre outras coisas, os protótipos das funções printf e scanfUma função pode ter um valor de retorno associado. Para ilustrar a discussão, vamos reescrevero código acima, fazendo com que a função fat retome o valor do fatorial.

#include <stdio.h>

int fat (int n); /* protótipo */

void main (void)

{

int n, r;

scanf ("%d", &n);

r = fat(n};

printf ("Fatorial = %d\n", r);

}

/* --- funcao fatorial-- */

int fat (int n){

int i;

int f = 1;

Página 18

Page 19: 02 Revisao C

for (i = 1; i <= n; i++)

f *= i;

return(f)

}

7 - USO DE PONTEIRO

Nesta seção apresentaremos uma revisão de ponteiros em linguagem C, o que servirá comobase para a implementação de listas encadeadas utilizando ponteiros.

Para cada tipo de dado existente, há um tipo ponteiro que pode armazenar endereços dememória onde existem valores do tipo correspondente armazenados. Por exemplo, quandoescrevemos: int a; declaramos uma variável com nome a que pode armazenar valores inteiros.Automaticamente, reserva-se um espaço na memória suficiente para armazenar valores inteiros(geralmente 2 bytes).

Da mesma forma que declaramos variáveis para armazenar inteiros, podemos declararvariáveis que, em vez de servirem para armazenar valores inteiros, servem para armazenarvalores de endereços de memória onde há variáveis inteiras armazenadas.

Para declararmos um ponteiro, usamos a mesma palavra do tipo com os nomes dasvariáveis precedidas pelo caractere *. Assim, podemos escrever: int *p para declarar um ponteirodo tipo int.

Neste caso, declaramos uma variável com nome p que pode armazenar endereços dememória onde existe um inteiro armazenado. Para atribuir e acessar endereços de memória.

Podemos usar o operador unário & ("endereço de"), que resulta no endereço da posição damemória reservada para a variável. O operador unário * ("conteúdo de"), aplicado a variáveis dotipo ponteiro, acessa o conteúdo do endereço de memória armazenado pela variável ponteiro.

Para trabalharmos com alocação dinâmica, devemos alocar porções de memória utilizandoa função malloc() que será mostrada a seguir.

main()

{

int *ptr, i;

ptr = (int *) malloc (sizeof(int));

*ptr = 5800;

i = *ptr;

printf ("%i", i); /* imprimirá 5800 */

i = 4200;

printf("%i",i); /* imprimirá 4200 */

}

Ao analisarmos o código acima notamos que a variável *ptr foi declarada como inteiro paraponteiro e na linha seguinte foi reservado 2 bytes para ptr que recebe o valor 5800. A variável irecebe o conteúdo de do endereço de ptr que é 5800, como podemos verificar pela impressão daantepenúltima linha.

main()

{

Página 19

Page 20: 02 Revisao C

int x;

*px;

...

px = &x;

}

O fragmento do código acima atribui o endereço de x para px. O conteúdo de px é oendereço da variável x. Vamos exemplificar o uso de ponteiro utilizando o fragmento de códigoabaixo.

main()

{

char c, *pc, x;

c = 'A';

pc = &c;

printf ("%c", *pc);

x = *pc;

}

Para compreendermos melhor o uso de ponteiros, vamos estudar o código do programaacima, observando os mapas de memórias na seqüência de quadros a seguir.

C pc x

1000 1001 1003

Lixo lixo Lixo

Fazemos a variável c receber o valor 'A' como segue: c = 'A'

C pc x

1000 1001 1003

'A' lixo Lixo

A variável pc recebe o endereço da variável c que é dado pela expressão seguinte: pc=&c:

C pc x

1000 1001 1003

'A' 1000

Ao imprimirmos o conteúdo da variável pc, teremos como saída, o valor 'A' que está noendereço 1000. Basta executar esta linha printf("%c ", *pc). Ao atribuirmos o conteúdo de pc (*pc)a variável x, temos x = c.

A seguir, apresentamos outros exemplos de uso de ponteiros. No código abaixo,declaramos duas variáveis do tipo inteiro, a primeira é um inteiro simples, a segunda é um inteiropara ponteiro. Em seguida a variável p, que é destinada a armazenar endereços, recebe oendereço da variável a. Ao atribuirmos 2 a *p, estamos colocando o valor 2 no endereço da

Página 20

Page 21: 02 Revisao C

variável a. Assim, o programa irá imprimir o valor 2. Sugerimos que implemente este código paracomprovar o que estamos apresentando.

int main (void)

{

int a;

int *p;

p = &a;

*p = 2;

printf ("%d", a);

return;

}

Agora que vimos como usar ponteiros, já podemos aplicar este conceito na utilização deestruturas para implementar listas. Abaixo apresentaremos a definição da estrutura node ou nó deuma lista encadeada. Após a definição, usamos a função malloc() para reservar uma porção dememória correspondente ao tamanho do nó da lista.

struct node{

int info;

struct node *prox;

};

struct node *ptr;

ptr = (struct node*) malloc (sizeof(struct node));

De maneira análoga, podemos declarar ponteiros de outros tipos: float *m; char * s, além deoutros tipos que podemos construir na linguagem C, como vimos acima com a função malloc() estruct;

8 - PASSAGEM DE PONTEIROS PARA FUNÇÓES

Os ponteiros oferecem meios de alterarmos valores de variáveis acessando-asindiretamente. As funções não podem alterar diretamente valores de variáveis da função que fez achamada. No entanto, se passarmos para uma função os valores dos endereços de memóriaonde suas variáveis estão armazenadas, a função pode alterar. indiretamente, os valores dasvariáveis da função que a chamou.

Vamos analisar o uso desta estratégia através de um exemplo. Consideremos uma funçãoprojetada para trocar os valores entre duas variáveis.

O próximo código não funciona como esperado (serão impressos 5 e 7), pois os valores dea e b da função main() não são alterados. Alterados são os valores de x e y dentro da funçãotroca, mas eles não representam as variáveis da função main(), apenas são inicializados com osvalores de a e b.

/* funcao troca (versao INCORRETA) */

#include<stdio. h>

void troca (int x, int y){

int temp;

Página 21

Page 22: 02 Revisao C

temp = x;

x = y;

y = temp;

}

void main ( void )

{

int a = 5, b = 7;

troca (a, b);

printf ("%d %d \n", a, b);

}

A alternativa é fazer com que a função receba os endereços das variáveis e, assim, alterarseus valores indiretamente. Reescrevendo o programa acima, corrigindo os erros, temos:

/* funcao troca (versao CORRETA) */

#include <stdio.h>

void troca (int *px, int *py ) {

int temp;

temp = *px;

*px = *py;

*py = temp;

}

void main (void)

{

int a = 5, b = 7;

troca(&a, &b); /* passamos os endereços das variáveis */

printf ("%d %d \n", a, b);

}

O código mostrado ilustra a execução deste programa mostrando o uso da memória.

Assim, conseguimos o efeito desejado. Agora fica explicado por que passamos o endereçodas variáveis para a função scanf(), pois, caso contrário, a função não conseguiria devolver osvalores lidos.

Para finalizar, vamos apresentar a passagem de estruturas por parâmetro. No código aseguir temos duas funções que recebem parâmetros. A função imp1() recebe um ponteiro paravariáveis simples ou primitiva, a outra função imp() recebe uma estrutura.

/ * passagem de parâmetros por referência * /

# include <stdio. h>

struct teste{ /* definição da estrutura teste * /

int m;

Página 22

Page 23: 02 Revisao C

char n[10];

};

void imp(struct teste te); /* protótipo da função imp. */

void imp1(int x, int y); /* protótipo da função imp1.*/

void main ()

{

int i, j;

struct teste te;

te.m = 5;

te.n[0] = 'T';

te.n[1] = 'e';

te.n[2] = 's';

te.n[3] = 't';

te.n[4] = 'e';

i = 10;

j = 20;

imp1(&i, &j);

imp(&te); /* passagem da estrutura */

getch();

}

/* recebe um endereço da estrutura teste como parâmetro */

void imp (struct teste *te)

{

int i;

printf ("Estrutura \n");

printf("%d\n",te->m);

for (i=0; i<5; i++)

printf ("%c" ,te->n [i]);

printf ("\n");

}

/* recebe os endereços das variáveis x e y e imprime os seusconteúdos*/

void impl(int *x, int *y){

printf("inteiros x e y\n");

Página 23

Page 24: 02 Revisao C

printf("%d\n",*x);

printf("%d\n",*y);

}

9 - LISTA DE EXERCÍCIOS (ENTREGAR MANUSCRITO)

a) Declarar um conjunto de variáveis inteiras, reais e char.

b) Construir um loop para realizar 10 iterações e imprimir somente os números pares dasinterações.

c) Calcular o somatório dos quadrados dos valores de um até n.

d) Construir um procedimento para imprimir o produto de dois valores recebidos porparâmetro.

e) Construir uma função que retorne o resto de uma divisão entre dois números inteiros.

f) Construir uma função que recebe como parâmetro o endereço de uma variável e altere ovalor para que a função principal possa imprimir.

g) Elabore uma função que receba uma estrutura por referência, altere os valores dosmembros da estrutura e faça a função principal imprimir esses valores alterados.

h) Elabore uma função que receba uma estrutura por valor, altere os valores dos membrosda estrutura e devolva para a função principal imprimir esses valores alterados.

10 - BIBLIOGRAFIA

SILVA, Osmar Quirino da.. Estrutura de Dados e Algoritmos Usando C: Fundamentos eAplicações. 1ª ed. Rio de Janeiro: Ciência Moderna, 2007.

Página 24