Bug de compilation C ++?

Eu tenho o seguinte código:

#include  #include  using namespace std; int main() { complex delta; complex mc[4] = {0}; for(int di = 0; di < 4; di++, delta = mc[di]) { cout << di << endl; } return 0; } 

Espero que ele produza “0, 1, 2, 3” e pare, mas gera uma série interminável de “0, 1, 2, 3, 4, 5, …..”

Parece que a comparação di<4 não funciona bem e sempre retorna true.

Se eu apenas comentar ,delta=mc[di] , recebo “0, 1, 2, 3” normalmente. Qual é o problema com a tarefa inocente?

Eu estou usando Ideone.com g ++ C + + 14 com opção -O2.

Isto é devido ao comportamento indefinido, você está acessando o array mc fora dos limites na última iteração do seu loop. Alguns compiladores podem executar otimização de loop agressiva em torno das suposições de nenhum comportamento indefinido. A lógica seria semelhante à seguinte:

  • Acessando mc fora dos limites é o comportamento indefinido
  • Não assuma nenhum comportamento indefinido
  • Portanto, di < 4 é sempre verdadeiro, caso contrário, mc[di] invocaria um comportamento indefinido

O gcc com a otimização ativada e usando o -fno-aggressive-loop-optimizations faz com que o comportamento do loop infinito desapareça ( veja-o ao vivo ). Enquanto um exemplo ao vivo com otimização, mas sem otimizações -fno-aggressive-loop- exibe o comportamento loop infinito que você observa.

Um exemplo ao vivo do código mostra que a verificação di < 4 é removida e substituída por um jmp incondicional:

 jmp .L6 

Isso é quase idêntico ao caso descrito no GCC pré-4.8 Breaks Broken SPEC 2006 Benchmarks . Os comentários para este artigo são excelentes e valem a pena ler. Ele observa que o clang capturou o caso no artigo usando -fsanitize=undefined que não posso reproduzir para este caso, mas gcc usando -fsanitize=undefined does ( veja-o ao vivo ). Provavelmente, o erro mais infame em torno de um otimizador fazendo uma inferência sobre o comportamento indefinido é a remoção da verificação do ponteiro nulo do kernel do Linux .

Embora isso seja uma otimização agressiva, é importante observar que, como o padrão C ++ diz que o comportamento indefinido é:

comportamento para o qual esta Norma não impõe requisitos

O que essencialmente significa que tudo é possível e nota ( ênfase minha ):

[...] O comportamento indefinido permissível varia de ignorar a situação completamente com resultados imprevisíveis , comportar-se durante a tradução ou execução do programa de uma maneira documentada característica do ambiente (com ou sem a emissão de uma mensagem de diagnóstico), para terminar uma tradução ou execução (com a emissão de uma mensagem de diagnóstico). [...]

Para obter um aviso do gcc, precisamos mover o cout fora do loop e, em seguida, vemos o seguinte aviso ( veja ao vivo ):

 warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations] for(di=0; di<4;di++,delta=mc[di]){ } ^ 

o que provavelmente teria sido suficiente para fornecer ao OP informações suficientes para descobrir o que estava acontecendo. Inconsistência como essa é típica dos tipos de comportamento que podemos ver com comportamento indefinido. Para entender melhor por que essa advertência pode ser inconsistente em face de um comportamento indefinido Por que você não avisa quando otimiza com base em comportamento indefinido? é uma boa leitura.

Note, -fno-aggressive-loop-optimizations está documentado nas notas de lançamento do gcc 4.8 .

Como você está incrementando di antes de usá-lo para indexar mc , pela quarta vez através do loop, você fará referência a mc [4], que está além do final de sua matriz, o que poderia levar a um comportamento problemático.

Você tem isto:

 for(int di=0; di<4; di++, delta=mc[di]) { cout<  

Tente isso:

 for(int di=0; di<4; delta=mc[di++]) { cout<  

EDITAR:

Para esclarecer o que está acontecendo Vamos quebrar a iteração do seu loop:

1ª iteração: Inicialmente di é definido como 0. Verificação de comparação: É di menor que 4? Sim, continue. Incremento di por 1. Agora di = 1. Pegue o elemento "nth" de mc [] e configure-o para ser delta. Desta vez, estamos pegando o segundo elemento, já que este valor indexado é 1 e não 0. Finalmente, execute o bloco de código / s dentro do loop for.

2 ª iteração: Agora di é definido como 1. Comparação de verificação: É di menor que 4? Sim e continue. Incremento di por 1. Agora di = 2. Pegue o elemento "nth" de mc [] e configure-o para ser delta. Desta vez, estamos pegando o terceiro elemento, já que este valor indexado é 2. Finalmente, execute o bloco de código / s dentro do loop for.

3ª iteração: Agora di está definido para 2. Verificação de comparação: É di menor que 4? Sim e continue. Incremento di por 1. Agora di = 3. Pegue o elemento "nth" de mc [] e configure-o para ser delta. Desta vez estamos pegando o 4º elemento já que este valor indexado é 3. Finalmente, execute o bloco de código / s dentro do loop for.

4ª iteração: Agora di está definido para 3. Verificação de comparação: É di menor que 4? Sim e continue. Incremento di por 1. Agora di = 4. (Você pode ver onde isso está indo?) Pegue o elemento "nth" de mc [] e configure-o para ser delta. Desta vez estamos pegando o 5º elemento, já que esse valor indexado é 4. Uh Oh, nós temos um problema; nosso tamanho de matriz é apenas 4. Delta agora tem lixo e isso é comportamento indefinido ou corrupção. Finalmente, execute o bloco de códigos dentro do loop for usando "garbage delta".

5ª iteração. Agora di está definido para 4. Verificação de comparação: É di menor que 4? Não, saia do circuito.

Corrupção por exceder limites de memory contígua (matriz).

É porque di ++ é executado na última execução do loop.

Por exemplo;

 int di = 0; for(; di < 4; di++); // after the loop di == 4 // (inside the loop we see 0,1,2,3) // (inside the for statement, after di++, we see 1,2,3,4) 

Você está acessando mc [] quando di == 4, então é um problema fora dos limites, potencialmente destruindo parte da pilha e corrompendo a variável di.

uma solução seria:

 for(int di = 0; di < 4; di++) { cout << di << endl; delta = mc[di]; }