Este é um comportamento indefinido?
arr[i++] += x; // [1]
// arr is an array of integer type and x is an integer type variable
// Assumptions -
// 1. Value of i is a valid index value of array arr and
// the result of prefix/postfix increment/decrement of i
// is also a valid index value of array arr.
// 2. The value of array members and variable x is such that
// arithmetic operation (+) does not result in overflow.
gcc
e clang
o compilador não fornece nenhuma mensagem de aviso na instrução [1]
, mas se eu substituir [1]
por esta instrução -
arr[i++] = arr[i++] + x; // [2]
eles emitem uma mensagem de alerta sobre esta afirmação, que é bastante óbvia -
gcc
compilador:
warning: operation on ‘i’ may be undefined [-Wsequence-point]
arr[i++] = arr[i++] + x;
clang
compilador:
warning: multiple unsequenced modifications to 'i' [-Wunsequenced]
arr[i++] = arr[i++] + x;
À primeira vista, parece que a declaração exibe comportamento indefinido, mas não é .
De c17#6.5.16.2 $4 [ênfase adicionada]
Verifique também a referência 1) abaixo.
Nesta instrução, o lvalue
arr[i++]
será avaliado apenas uma vezSerá tratado como
Da mesma forma, se o incremento de prefixo for usado, a instrução
será tratado como
Portanto, a modificação no valor de
i
não deixa de ser sequenciada para uma modificação diferente no valor dei
ou para o uso do valor dei
para computaçãoarr[i]
. [ referência 1) ]Portanto, a instrução
arr[i++] += x;
não exibe Comportamento Indefinido .[ O mesmo vale para decremento de prefixo/postfix se usado na instrução
arr[i++] += x;
. ]Considerando que, na declaração
[2]
-a avaliação de LHS e RHS
i++
não é sequenciada: podem ser realizadas em qualquer ordem (dentro de um único thread de execução, o compilador pode intercalar as instruções da CPU que compõem ai++
avaliação de LHS e RHS). Se um efeito colateral em um objeto escalar não for sequenciado em relação a outro efeito colateral no mesmo objeto escalar, o comportamento será indefinido e, portanto, o compilador estará avisando sobre ele.Observe que as declarações a seguir também exibem comportamento indefinido
pelo mesmo motivo explicado acima.
1). Referência:
c17#6,5$2
c17#6.5.16$3
está bem definido; nenhum objeto é modificado mais de uma vez entre pontos de sequência. É efetivamente equivalente a
com a ressalva de que as duas últimas atualizações podem ocorrer em qualquer ordem, até mesmo simultaneamente.
Por contraste,
resulta em comportamento indefinido porque
i
é modificado mais de uma vez entre pontos de sequência. Atée
resulta em comportamento indefinido porque
i
está sendo atualizado e usado em um cálculo de valor entre pontos de sequência.Não é garantido que as expressões aritméticas sejam avaliadas da esquerda para a direita, nem é garantido que os efeitos colaterais sejam aplicados imediatamente após a avaliação; as expressões acima podem produzir mais de um resultado e, no que diz respeito à definição da linguagem, todos esses resultados diferentes são igualmente corretos.
Para torná-los bem definidos, separe a atualização
i
da atualização dearr[i]
:Ou apenas use