Algoritmos e Estruturas de Dados I
Os nomes em C consistem de seqüências de caracteres iniciadas com letras. Os caracteres
válidos são letras e dígitos e o sublinhado. Vários elementos da linguagem
podem ter nomes. Os nomes podem ser definidos no escopo de elementos que também
podem ter nomes.
No decorrer da disciplina serão apresentados diversos
espaços de nomes ou “escopos”.
Uma expressão corresponde a um trecho de programa que calcula um certo valor e
ainda pode produzir efeitos colaterais. As expressões podem ser utilizadas em
vários pontos de um programa. O valor produzido por uma expressão tem associado
a ele um certo tipo. As expressões mais simples correspondem a um literal
(ou constante) ou uma variável e neste caso o tipo da expressão (ou tipo do
valor retornado) corresponde ao tipo do literal ou ao tipo da variável. Quando
as expressões envolvem operadores o tipo da expressão corresponde à uma
combinação razoavelmente complexa de regras.
Exemplos:
Uma expressão pode ser usada no lado direito de um comando de atribuição:
<variavel = <expressao>; O tipo da variável e o tipo da expressão têm
que ser "compatíveis".
int a;
a=3; /* a expressão corresponde a uma constante representada por um literal
*/
double b;
b=3.1415;
double c=2.;
double d;
d=c; /* a expressão corresponde a uma variável */
Conforme dito acima uma
expressão pode corresponder a um literal ou uma variável; uma expressão pode
correponder ainda a uma chamada de função ou ainda a uma combinação de
literais, variáveis e chamadas de funções; A combinação é feita através de
"operadores". Existem ainda várias outras maneiras de definir
expressões que serão vistas mais à frente na disciplina.
Uma chamada de funcão corresponde a um desvio do fluxo de
execução para um trecho de código que irá calcular um valor e retorná-lo no
ponto em que o fluxo de controle também retorna. Cada função tem 0, 1 ou mais
argumentos (ou parâmetros) na sua definição. O compilador verifica se existe
uma expressão para cada argumento do método invocado. A sintaxe para o conjunto
de argumentos consiste em uma lista de expressões separadas por vírgula e
delimitada por abre e fecha parênteses.
Exemplos de expressões
correspondentes a chamadas de funções:
int a;
a=max(v1,v2); /* retorna v1 caso v1>=v2 ou retorna v2 caso v2>=v1 */
int b;
b=max(max(v1,v2), max(v3,v4));
Na expressão
max(max(v1,v2), max(v3,v4))
temos duas chamadas de uma função aninhadas dentro de uma outra chamada
de função. A função max() definida para dois argumentos e retorna o maior
destes argumentos.
(A linguagem C permite a
definição de “macros” e max poderia ser uma macro:
#define max(A, B) ((A) > (B) ? (A) :
(B))
Quando o fluxo de controle
atinge uma chamada de função (em algum momento o código fonte da função
correspondente foi compilado e o código executável da função está disponível):
·
primeiro
são avaliadas as expressões correspondentes aos argumentos da função,
·
é
registrado ou salvo o ponto de retorno do fluxo de controle e feito o desvio
para o código da função.
·
A
execução normal de uma função termina com a função retornando o fluxo de
controle para o ponto de retorno salvo juntamente com o retorno do valor
calculado.
Na linguagem C as
expressões correspondentes aos argumentos são calculadas da esquerda para a
direita ou da direita para a esquerda?.
Descreva o valor impresso
pelo seguinte trecho de programa:
int i=2;
int j=3;
printf(“%d %d\n”, i=3,i+j);
considere o seguinte trecho
de programa
int
i=3;
int ai[5];
ai[i]=i++;
Qual elemento do arranjo
recebe a atribuição? ai[3] ou ai[4]?
ai[i]=++i;
Conforme já discutido, a sintaxe
de um comando de atribuição corresponde a (i)uma variável seguida do
(ii)símbolo "=" (denominado operador de atribuição), seguido de
(iii)uma expressão; Quando o fluxo de controle atinge um comando de atribuição
o valor da expressão é calculado e atribuído à variável, além disso o comando
de atribuição tem associado a ele um valor que é exatamente o valor da
expressão, é isso que permite aparecer em um programa expressões do tipo: v1=v2=3;
Constantes, variáveis e
chamadas de função podem ser combinadas através de operadores para construir
expressões mais complexas. Alguns operadores são bastante intuitivos. Considere
o seguinte trecho de programa:
int i=2;
int j=3;
int k;
k=i+j;
O operador
"+" na expressão acima corresponde a uma soma de valores do tipo int.
Operadores aritméticos
+ - * / %
O operador % corresponde ao resto da divisão entre os
operandos correspondentes. A avaliação da expressão 11 % 3 resulta no valor 2,
Operadores de incremento e
decremento
++ (incremento) -- (decremento)
Exemplos:
int i=3; /* é atribuido o valor 3
para a variável i, a expressão vale 3*/
i++; /* é atribuido o valor 4 para a variável i, a expressão vale 3 */
++i; /* é atribuido o valor 5 para a variável i, a expressão vale 5 */
i--; /* é atribuido o valor 4 para a variável i, a expressão vale 5 */
--i; /* é atribuido o valor 3 para
avariável i, a expressão vale 3 */
int a=0;
int b=0;
int c=a++ + b++;
printf(“%d”,c); // imprime 0
Operadores relacionais
== (igual) != (diferente) < (menor) > (maior) <= (menor
ou igual) >= (maior ou igual)
Em alguns casos as
expressões podem não ser intuitivas. Esperamos que X==X retorne verdadeiro e
X!=X retorne falso, mas quando X corresponde a um double NaN, X!=X retorna
verdadeiro!
Operadores Lógicos
& (e lógico)
&& (e condicional) | (ou lógico) || (ou condicional)
! (negação lógica)
X & Y, X | Y -> a
expressão X e a expressão Y são avaliadas
X && Y ->Se a expressão X for “false” então a expressão Y não é
avaliada
X || Y ->Se a expressão X for “true” então a expressão Y não é avaliada
Operador condicional
?:
<exp1>?<exp2>:<exp3>
Exemplos
int i;
int v1=3, v2=4;
i=v1>v2?v1:v2; /* atribui o maior valor à variável i */
Operadores de atribuição
= += -= *= /= %= &= != |= &&=
||=
Operadores de elencamento
() - abre e fecha parênteses
O trecho de programa abaixo
deve ser evitado por um bom programador. Mas (infelizmente) tal trecho pode ser
formulado. O que é impresso pelo trecho abaixo?
int i=2;
int j=(i=3)*i;
printf(“%d”, j);
Considere a tabela abaixo. Operadores na mesma linha têm igual precedência,
operadores nas linhas superiores têm precedência sobre operadores nas linhas
inferiores:

Existem
ainda vários outros tipos de operadores que serão discutidos ao longo da
disciplina. Alguns deles poderiam ser discutidos desde já, tais como os
operadores de deslocamento (<<, >>, >>>), outros
operadores serão discutidos em um contexto bem especializado
Um dos
problemas que devem ser enfrentados com expressões envolvendo os tipos double e
float são os erros de arredondamento