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;
Foi útil?

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

  1. cuspir para fora uma mensagem de erro,
  2. Fazer implementação algo definido e documentado,
  3. 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    
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top