Expressões

Autor
Afiliação

Diogo Silva

Academia da Força Aérea

Slides

O que são?

Expressões devolvem sempre um valor concreto com um determinado tipo.

    int raio = 2;
    float area = 3.14 * raio * raio;

Operações aritméticas

1+2    // soma
3.14-2 // subtração
1*2.3  // multiplicação
5/2    // divisão, devolve 2 -> porquê?
5%2    // resto da divisão

int v = 2;
-v     // torna valor negativo, operação unária

Operação unária ➡ Operação que recebe apenas 1 operando, e.g. -v

Por outro lado, as outras operações são binárias ➡ 2 operandos, e.g. 1+2


Cuidado com a divisão!
  • O operador para a divisão, quando recebe 2 operandos inteiros, devolve sempre um valor inteiro (ignorando, sem arredondamento, a parte decimal)

    5/2 // devolve 2
  • Quando misturamos um inteiro com um real, o inteiro é automaticamente convertido e o resultado será o valor preciso

    5.0/2 // devolve 2.5

O resto da divisão % requer 2 operandos inteiros.

Usar 0 como o segundo operando da divisão / ou resto da divisão %, i.e. divisão por 0, gera comportamento não definido.


Precedência e associatividade

  • Qual o valor desta expressão?

    1+2*-2
  • Evitem escrever expressões deste tipo. Usem ( ) para melhorar legibilidade.

  • Melhor?

    1 + (2 * -2)

As operações no C têm uma precedência.

Precedência Nome Operadores Associatividade
1 incremento (sufixo) ++ -- esquerda
2 incremento (prefixo)
additivos unários
++ --
+ -
direita
3 multiplicativos * / % esquerda
4 aditivos binários + - esquerda
5 desigualdades < <= > >= esquerda
6 igualdades == != esquerda
7 atribuição = *= /= %= += -= direita


Exemplo de precedência de operadores. Fonte: geeksforgeeks.org

Exemplos:

  • 1 + 2 * -21 + (2 * (-2))
  • i + j * ki + (j * k)
  • -i * -j(-i) * (-j)

E quando uma expressão tem 2 operações com a mesma precedência?

i - j + k

i / j * k 

Tem-se em conta a associatividade.

Exemplo de associatividade de operadores. Fonte: geeksforgeeks.org

i / j * k == (i / j) * k

+ - * / % têm associtividade à esquerda, logo agrupam o que está antes do operador.

Os operadores unários têm associatividade à direita.

- -2 == - (-2)


Exemplo de associatividade e precedência de operadores. Fonte: geeksforgeeks.org

Operadores de atribuição

Já conhecemos o operador de atribuição simples =.

É o que designamos por uma operação com efeitos secundários, porque altera o estado dos operandos.


a = 2 altera o estado da variável a porque o seu valor é agora 2.

Adicionalmente, a = 2 é uma expressão válida que devolve o valor que foi atribuido à variável

int a=10;
printf("a=%d\n", a=2); // escreve 2 na consola e altera o valor de a

Todas as operações de atribuição exigem que o operando da esquerda seja uma variável.


Existem outros que se combinam com operações aritméticas.

Expressão Descrição
var+=3 soma 3 ao valor de var e atribui o resultado a var
var-=3 subtrai 3 ao valor de var e atribui o resultado a var
var*=3 multiplica 3 com o valor de var e atribui o resultado a var
var/=3 divide o valor de var por 3 e atribui o resultado a var
var%=3 faz o resto da divisão entre var e 3 e atribui o resultado a var


  • v*=3 não é equivalente a v = v * 3, embora na maior parte das utilizações o resultado seja o mesmo. Porquê?

  • Observem a expressão v *= 3 + 10. É equivalente a v = v * 3 + 10?

  • Devido às regras de precedência e associatividade leva a um resultado diferente.

    int a=3;
    printf("a=%d\n", (a = a * 3 + 10)); // a=19
    a=3;
    printf("a=%d\n", a*=3+10); // a=39

Não confundir += com =+.

A primeira operação acumula o valor à direita.

A segunda é uma simples atribuição seguida do operador + unário.


Em suma…

… usem sempre ( ) para deixar claro qual é a ordem das operações.


Incremento e decremento

Existe uma instrução que é frequentemente usada no C (irão perceber porquê quando abordarmos ciclos):

i = i + 1;
i += 1;

Existe outra forma de escrever esta instrução, com o operador de incremento ++

i = i + 1;
i += 1;
i++;

Este operador pode ser usado tanto antes de uma variável (pré-incremento) como depois (pós-incremento).

Qual será a diferença?

int i=10;
printf("%d\n", ++i); // ?
printf("%d\n", i);   // ?
i=10;
printf("%d\n", i++); // ?
printf("%d\n", i);   // ?

int i=10;
printf("%d\n", ++i); // 11
printf("%d\n", i);   // 11
i=10;
printf("%d\n", i++); // 10
printf("%d\n", i);   // 11

Além do incremento ++, também temos disponível o decremento --, que funciona da mesma forma, mas subtrai em uma unidade.


Avaliação de expressões


Com este conhecimento conseguimos avaliar expressões complicadas.

a= b += c++ - d + --e / -f

  1. Encontrar o operador com maior precedência.
  2. Colocar ( ) à volta do operador e operandos.
    1. Aplicar regras de precedência e associatividade.
  3. Repetir.

a= b += c++ - d + --e / -f

  1. a= b += (c++) - d + (--e) / -f c++ e depois --e (precedência)
  2. a= b += (c++) - d + (--e) / (-f) operador unário -f (precedência)
  3. a= b += (c++) - d + ((--e) / (-f)) divisão (precedência)

  1. a= b += (c++) - d + ((--e) / (-f))
  2. a= b += ((c++) - d) + ((--e) / (-f)) - e + com a mesma precedência e com operando em comum d (precedência), agrupam da esquerda para a direita, logo - primeiro (associatividade)
  3. a= b += (((c++) - d) + ((--e) / (-f))) + (precedência)
  4. a= (b += (((c++) - d) + ((--e) / (-f)))) = e += com mesma precedência (precedência), agrupam da direita para a esquerda, logo += primeiro (associatividade)
  5. (a= (b += (((c++) - d) + ((--e) / (-f))))) sobra apenas o = (precedência)

Moral da história: façam um favor a vós próprios…

. . .

… e usem ( ).