É indefinido porque modifica x
duas vezes entre os pontos de sequência. O padrão diz que é indefinido, portanto é indefinido.
Isso eu sei.
Mas por que?
Meu entendimento é que proibir isso permite que os compiladores otimizem melhor. Isso poderia ter feito sentido quando C foi inventado, mas agora parece um argumento fraco.
Se reinventássemos o C hoje, faríamos dessa maneira ou será melhor?
Ou talvez haja um problema mais profundo, que dificulta a definição de regras consistentes para essas expressões, por isso é melhor proibi-las.
Então, suponha que reinventássemos C hoje. Gostaria de sugerir regras simples para expressões como x=x++
, que me parecem funcionar melhor do que as regras existentes.
Gostaria de obter sua opinião sobre as regras sugeridas em comparação com as existentes ou com outras sugestões.
Regras sugeridas:
- Entre os pontos de sequência, a ordem da avaliação não é especificada.
- Os efeitos colaterais ocorrem imediatamente.
Não há comportamento indefinido envolvido. As expressões são avaliadas com esse valor ou com isso, mas certamente não formatarão seu disco rígido (estranhamente, nunca vi uma implementação em que formate x=x++
o disco rígido).
Expressões de exemplo
x=x++
- Bem definido, não mudax
.
Primeiro,x
é incrementado (imediatamente quandox++
é avaliado) e, em seguida, seu valor antigo é armazenadox
.x++ + ++x
- Incrementax
duas vezes, avalia como2*x+2
.
Embora ambos os lados possam ser avaliados primeiro, o resultado éx + (x+2)
(lado esquerdo primeiro) ou(x+1) + (x+1)
(lado direito primeiro).x = x + (x=3)
- Não especificado,x
defina comox+3
ou6
.
Se o lado direito é avaliado primeiro, éx+3
. Também é possível quex=3
seja avaliado primeiro, por isso é3+3
. Em qualquer um dos casos, ax=3
atribuição acontece imediatamente quandox=3
é avaliada; portanto, o valor armazenado é substituído pela outra atribuição.x+=(x=3)
- Bem definido, definex
como 6.
Você pode argumentar que isso é apenas uma abreviação para a expressão acima.
Mas eu diria que isso+=
deve ser executado depoisx=3
, e não em duas partes (leiax
, avaliex=3
, adicione e armazene novo valor).
Qual é a vantagem?
Alguns comentários levantaram esse ponto positivo.
Eu certamente não acho que expressões como x=x++
devam ser usadas em qualquer código normal.
Na verdade, eu sou muito mais rigorosa do que isso - Eu acho que o único bem de uso para x++
em tão x++;
sozinho.
No entanto, acho que as regras de linguagem devem ser o mais simples possível. Caso contrário, os programadores simplesmente não os entenderão. a regra que proíbe alterar uma variável duas vezes entre os pontos de sequência é certamente uma regra que a maioria dos programadores não entende.
Uma regra muito básica é a seguinte:
se A é válido e B é válido, e eles são combinados de maneira válida, o resultado é válido.
x
é um valor L válido, x++
é uma expressão válida e =
é uma maneira válida de combinar um valor L e uma expressão; então, como x=x++
é que isso não é legal?
O padrão C faz uma exceção aqui, e essa exceção complica as regras. Você pode pesquisar stackoverflow.com e ver o quanto essa exceção confunde as pessoas.
Então eu digo - livre-se dessa confusão.
=== Resumo das respostas ===
Por que fazer isso?
Tentei explicar na seção acima - quero que as regras C sejam simples.Potencial de otimização:
isso requer alguma liberdade do compilador, mas eu não vi nada que me convenceu de que isso poderia ser significativo.
A maioria das otimizações ainda pode ser feita. Por exemplo,a=3;b=5;
pode ser reordenado, mesmo que o padrão especifique o pedido. Expressões comoa=b[i++]
ainda podem ser otimizadas da mesma forma.Você não pode alterar o padrão existente.
Eu admito, não posso. Eu nunca pensei que realmente pudesse ir em frente e mudar padrões e compiladores. Eu só queria pensar se as coisas poderiam ter sido feitas de maneira diferente.
x
a si próprio e, se você quiser incrementar,x
pode apenas dizerx++;
- não há necessidade da atribuição. Eu diria que não deve ser definido apenas porque seria difícil lembrar o que deveria acontecer.