i = i + ++ ++ i; em C ++
-
19-08-2019 - |
Pergunta
Alguém pode me explicar por que este código imprime 14? Eu estava apenas perguntado por um outro estudante e não conseguia entender.
int i = 5;
i = ++i + ++i;
cout<<i;
Solução
A ordem de efeitos colaterais não é definida em C ++. comportamento Além disso, a modificação de uma variável duas vezes numa única expressão não tem definido (Ver o C ++ padrão, §5.0.4, página física 87 / lógica página 73).
Solução: Não use efeitos colaterais na expressão complexa, não use mais de um em mais simples. E não faz mal para permitir que todos os avisos que o compilador pode dar-lhe: Adicionando -Wall
(gcc) ou /Wall /W4
(Visual C ++) para a linha de comando produz um aviso apropriado:
test-so-side-effects.c: In function 'main':
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
Obviamente, os compila código para:
i = i + 1;
i = i + 1;
i = i + i;
Outras dicas
Isso de um comportamento indefinido, o resultado irá variar dependendo do compilador que você usa. Veja, por exemplo, C ++ FAQ Lite .
Em algumas das respostas / comentários, tem havido uma discussão sobre o significado de 'comportamento indefinido' e se isso faz com que o inválido programa. Então, eu estou postando esta resposta bastante longo detalhando exatamente o que o padrão diz com algumas notas. Espero que não seja muito chato ...
Os bits cotados da norma vir do atual C ++ padrão (ISO / IEC 14882: 2003). Há redacção semelhante no padrão C.
De acordo com o padrão C ++, a modificação de um valor de mais do que uma vez dentro de um conjunto de pontos de sequência resulta em comportamento indefinido (secção 5 N. ° 4):
exceto onde indicado, a ordem de avaliação de operandos de indivíduo operadores e subexpressions de expressões individuais, ea ordem em que os efeitos secundários ocorrem, é unspecified.53) Entre o anterior e próxima sequência de apontar um escalar objeto deve ter um valor armazenado modificada no máximo uma vez pela avaliação de uma expressão. Além disso, o valor anterior será acessado apenas para determinar o valor para ser armazenado. Os requisitos desta parágrafo serão atendidos para cada uma ordenação admissível do subexpress~oes de uma expressão completo; caso contrário, o comportamento é indefinido. [Exemplo:
i = v[i++]; // the behavior is unspecified i = 7, i++, i++; // i becomes 9 i = ++i + 1; // the behavior is unspecified i = i + 1; // the value of i is incremented
exemplo -end]
Note-se que o segundo exemplo, "i = 7, i++, i++;
" é definida desde o operador de vírgula é um ponto de sequência.
Aqui está o que o padrão C ++ diz 'comportamento indefinido' significa:
1.3.12 comportamento indefinido [defns.undefined]
comportamento, como pode surgir quando da utilização de uma construção de programa errôneo ou dados errados, para o qual este Norma não impõe requisitos. comportamento indefinido também pode ser esperado quando este Norma omite a descrição de qualquer definição explícita de comportamento. [Nota: permissível indefinido gamas de comportamento de ignorando a situação completamente com resultados imprevisíveis, para comportar durante tradução ou a execução do programa de uma forma característica documentada do ambiente (com ou sem a emissão de uma mensagem de diagnóstico), para terminar a tradução ou a execução (com a emissão de um mensagem de diagnóstico). Muitas construções programa errôneas fazer comportamento não engendram indefinido; eles são obrigados a ser diagnosticada. ]
Em outras palavras, o compilador é livre para fazer o que quiser, incluindo
- cuspir para fora uma mensagem de erro,
- Fazer implementação algo definido e documentado,
- ter resultados completamente imprevisíveis
A segunda rubrica abrange extensões de linguagem que a maioria dos compiladores têm, mas é claro que não estão definidos na norma.
Então eu acho que algo estritamente falando que o comportamento exposições indefinido não é 'ilegal', mas na minha experiência, sempre houve alguma coisa em um programa C / C ++ que exibe 'comportamento indefinido' (a menos que seja uma extensão) - é um bug . Eu acho que chamar tal construção ilegal não é confuso, enganosas ou equivocada.
Além disso, eu acho que tentar explicar o que o compilador está fazendo para atingir o valor 14 não é particularmente útil, que perde o ponto. O compilador poderia fazer quase qualquer coisa; na verdade, é provável que o compilador pode chegar a um resultado diferente quando ele é executado usando diferentes opções de otimização (ou pode produzir código que acidentes - quem sabe).
Para aqueles que querem algumas referências adicionais ou um apelo à autoridade, aqui estão algumas dicas:
O Steve Summit (mantenedor do comp.lang.c Perguntas Frequentes) longa, longa resposta sobre este tema a partir de 1995:
Aqui está o que Bjarne Stroustrup tem a dizer sobre o assunto:
Nota de rodapé :. O C ++ utilizações comuns a palavra 'ilegais' exatamente uma vez - ao descrever a diferença entre C ++ e Standard C em relação ao uso de static
ou extern
com declarações de tipo
Simples ... você compilador está avaliando ambos incrementos antes de executar a soma, sem cache os resultados intermediários. Isto significa que quando você adiciona i duas vezes, agora tem o valor de 7.
Se você fizer
int j=++i;
int k=++i;
i = j+k;
Você verá 13 como esperado.
Em seu compilador particular, ele está optando por fazer as duas operações ++ primeiro, depois a adição. É interpretar o código como:
int i = 5;
++i;
++i;
i = i + i;
cout << i;
Isso é perfeitamente válido.
A melhor pergunta seria, não é sempre vai ser 14
?
int i = 5;
i = ++i + ++i;
cout<<i;
i = ++i + ++i ;
i = ++(5) + ++(5) ;
i = 6 + 6 ;
i = 12;
i = ++i + ++i ;
i = ++i + ++(5) ;
i = ++i + (6) ;
i = ++(6) + 6 ;
i = (7) + 6 ;
i = 13;
i = ++i + ++i ;
i = ++i + ++(5) ;
i = ++(6) + (6) ;
i = (7) + (7) ;
i = 14;
Em toda a probabilidade será provavelmente 14
, porque torna ligeiramente mais sentido.
Eu acho que quando se olha para o problema a partir da visão da árvore de sintaxe, a resposta para o problema torna-se mais claro:
i
|
=
|
+
|
expressão unária - unário expressão
expressão unária: operador unário expressão
No nosso caso, a expressão resume-se à variável i.
Agora, o que acontece é que tanto a expressão unária modificar o mesmo operando, para que o código faz duas vezes ++ i ao avaliar as expressões unárias antes de adicionar os resultados de ambas as expressões unários.
Então, o que o código faz é realmente
++ i;
++ i;
i = i + i;
Para i = 5 que meios
i = i + 1; // i <- 6
i = i + 1; // i <- 7
i = i + i; // i <- 14
Uma vez que o incremento de prefixo tem precedência:
int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14
i = i++ + i; //11
i = i++ + i++; //12
i = i++ + ++i; //13
i = ++i + i++; //13
i = ++i + ++i; //14