Como a precedência do operador C quebra seu código (e como corrigi-lo)

6

Você escreve o que quer dizer. O compilador lê o que deseja.

Se você já olhou para uma linha de código C, convencido de que ela estava fazendo uma coisa, apenas para vê-la fazer algo totalmente diferente, você encontrou a precedência do operador. Não é um bug. É uma característica da linguagem que incomoda até mesmo desenvolvedores experientes.

Faça esta aritmética simples:

x=5+3*6;

A aula de matemática diz adicionar primeiro. C diz que não. A multiplicação e a divisão estão em um nível mais alto do que a adição e a subtração. O compilador multiplica 3 por 6 primeiro. Em seguida, soma 5. X obtém 23. Não 48. Você acabou de perder pontos em um teste. Ou pior, uma implantação de produção.

A confusão aumenta quando você mistura operadores com sintaxe.

Veja esta declaração:

char *a[10];

a é um ponteiro para uma matriz de 10 caracteres? Ou é uma matriz de 10 ponteiros para caracteres?

Sem conhecer as regras de precedência específicas, você está adivinhando. Em C, os colchetes [] se vinculam com mais força do que os ponteiros *. Portanto, esta é uma matriz de 10 ponteiros para caracteres.

Isso é importante porque C não analisa como o inglês. Ele analisa como uma hierarquia rígida.

Você pode escrever um código que pareça correto, mas falhe silenciosamente devido à precedência.

Considere aritmética de ponteiro misturada com acesso à estrutura.

*pi = 10;

Isso não funciona como esperado. O operador de ponto . tem precedência maior que o operador de desreferência *. O compilador tenta acessar o membro i do objeto apontado por p antes de desreferenciar p. Mas p é um ponteiro, não uma estrutura. O compilador engasga. Ou pior, interpreta a memória de uma forma que corrompe os dados.

Você tem que forçar o problema.

(*p).i = 10;

Os parênteses vencem. Sempre.

A Hierarquia dos Operadores

Kernighan e Ritchie, os arquitetos de C, estabeleceram a precedência em seu texto clássico. A tabela abaixo mostra a ordem. A linha superior é a mais alta. O fundo é o mais baixo.

  1. Parênteses () e Subscritos []
  2. Da esquerda para a direita.
  3. Operadores Unários ++, --, +, -, !, ~, (tipo), * (desreferência), & (endereço), sizeof
  4. Da direita para a esquerda.
  5. Multiplicativo *, /, %
  6. Da esquerda para a direita.
  7. Aditivo +, -
  8. Da esquerda para a direita.
  9. Mudança <<, >>
  10. Da esquerda para a direita.
  11. Relacional <, <=, >, >=
  12. Da esquerda para a direita.
  13. Igualdade ==, !=
  14. Da esquerda para a direita.
  15. E bit a bit &
  16. Da esquerda para a direita.
  17. XOR bit a bit ^