C ++ 98 e C ++ 03
Esta resposta é para as versões mais antigas do padrão C ++. As versões C ++ 11 e C ++ 14 do padrão não contêm formalmente 'pontos de sequência'; operações são 'sequenciadas antes' ou 'não sequenciadas' ou 'indeterminadamente sequenciadas'. O efeito líquido é essencialmente o mesmo, mas a terminologia é diferente.
Disclaimer : Ok. Essa resposta é um pouco longa. Portanto, tenha paciência ao lê-lo. Se você já sabe essas coisas, lê-las novamente não o deixará louco.
Pré-requisitos : Um conhecimento elementar do Padrão C ++
O que são pontos de sequência?
O Padrão diz
Em certos pontos especificados na sequência de execução chamados pontos de sequência , todos os efeitos colaterais das avaliações anteriores devem estar completos e nenhum efeito colateral das avaliações subsequentes deve ter ocorrido. (§1.9 / 7)
Efeitos colaterais? Quais são os efeitos colaterais?
A avaliação de uma expressão produz algo e se, além disso, há uma alteração no estado do ambiente de execução, diz-se que a expressão (sua avaliação) tem alguns efeitos colaterais.
Por exemplo:
int x = y++; //where y is also an int
Além da operação de inicialização, o valor de y
é alterado devido ao efeito colateral do ++
operador.
Por enquanto, tudo bem. Passando para os pontos de sequência. Uma definição de alternância de pontos seq fornecida pelo autor comp.lang.c Steve Summit
:
O ponto de sequência é um momento no qual a poeira se depositou e todos os efeitos colaterais vistos até agora são garantidos.
Quais são os pontos de sequência comuns listados no padrão C ++?
Esses são:
no final da avaliação da expressão completa ( §1.9/16
) (Uma expressão completa é uma expressão que não é uma subexpressão de outra expressão.) 1
Exemplo:
int a = 5; // ; is a sequence point here
na avaliação de cada uma das seguintes expressões após a avaliação da primeira expressão ( §1.9/18
) 2
a && b (§5.14)
a || b (§5.15)
a ? b : c (§5.16)
a , b (§5.18)
(aqui a, b é um operador de vírgula; in func(a,a++)
,
não é um operador de vírgula, é apenas um separador entre os argumentos a
e a++
. Portanto, o comportamento é indefinido nesse caso (se a
for considerado um tipo primitivo))
em uma chamada de função (esteja a função em linha ou não), após a avaliação de todos os argumentos da função (se houver) que ocorrem antes da execução de quaisquer expressões ou instruções no corpo da função ( §1.9/17
).
1: Nota: a avaliação de uma expressão completa pode incluir a avaliação de subexpressões que não fazem parte lexicamente da expressão completa. Por exemplo, subexpressões envolvidas na avaliação de expressões de argumento padrão (8.3.6) são consideradas criadas na expressão que chama a função, não na expressão que define o argumento padrão
2: Os operadores indicados são os operadores internos, conforme descrito na seção 5. Quando um desses operadores é sobrecarregado (seção 13) em um contexto válido, designando assim uma função de operador definida pelo usuário, a expressão designa uma chamada de função e os operandos formam uma lista de argumentos, sem um ponto de sequência implícito entre eles.
O que é comportamento indefinido?
O Padrão define Comportamento Indefinido na Seção §1.3.12
como
comportamento, como pode surgir com o uso de uma construção de programa ou dados errados, para os quais esta Norma Internacional não impõe requisitos 3 .
Um comportamento indefinido também pode ser esperado quando esta Norma Internacional omite a descrição de qualquer definição explícita de comportamento.
3: o comportamento indefinido permitido varia de ignorar completamente a situação com resultados imprevisíveis, comportar-se durante a tradução ou a execução do programa de maneira documentada característica do ambiente (com ou sem a emissão de uma mensagem de diagnóstico), até o término de uma tradução ou execução (com a emissão de uma mensagem de diagnóstico).
Em resumo, um comportamento indefinido significa que qualquer coisa pode acontecer, desde daemons voando pelo nariz até a namorada engravidar.
Qual é a relação entre comportamento indefinido e pontos de sequência?
Antes de entrar nesse assunto, você deve conhecer as diferenças entre comportamento indefinido, comportamento não especificado e comportamento definido para implementação .
Você também deve saber disso the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified
.
Por exemplo:
int x = 5, y = 6;
int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.
Outro exemplo aqui .
Agora, o padrão §5/4
diz
- 1) Entre o ponto de sequência anterior e o próximo, um objeto escalar deve ter seu valor armazenado modificado no máximo uma vez pela avaliação de uma expressão.
O que isso significa?
Informalmente, significa que entre dois pontos de sequência uma variável não deve ser modificada mais de uma vez. Em uma declaração de expressão, next sequence point
geralmente está no ponto-e-vírgula final e previous sequence point
no final da declaração anterior. Uma expressão também pode conter intermediários sequence points
.
Na sentença acima, as seguintes expressões invocam o comportamento indefinido:
i++ * ++i; // UB, i is modified more than once btw two SPs
i = ++i; // UB, same as above
++i = 2; // UB, same as above
i = ++i + 1; // UB, same as above
++++++i; // UB, parsed as (++(++(++i)))
i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)
Mas as seguintes expressões são boas:
i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i); // well defined
int j = i;
j = (++i, i++, j*i); // well defined
- 2) Além disso, o valor anterior deve ser acessado apenas para determinar o valor a ser armazenado.
O que isso significa? Isso significa que, se um objeto é gravado em uma expressão completa, todo e qualquer acesso a ele dentro da mesma expressão deve estar diretamente envolvido no cálculo do valor a ser gravado .
Por exemplo, em i = i + 1
todo o acesso de i
(no LHS e no RHS) estão diretamente envolvidos no cálculo do valor a ser gravado. Então está tudo bem.
Essa regra restringe efetivamente expressões legais àquelas em que os acessos precedem comprovadamente a modificação.
Exemplo 1:
std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2
Exemplo 2:
a[i] = i++ // or a[++i] = i or a[i++] = ++i etc
é proibido porque um dos acessos de i
(aquele em a[i]
) não tem nada a ver com o valor que acaba sendo armazenado em i (o que acontece em i++
), e, portanto, não há uma boa maneira de definir - tanto para o nosso entendimento quanto para o compilador - se o acesso deve ocorrer antes ou depois do valor incrementado ser armazenado. Portanto, o comportamento é indefinido.
Exemplo 3:
int x = i + i++ ;// Similar to above
Resposta de acompanhamento para C ++ 11 aqui .
*p++ = 4
não é um comportamento indefinido.*p++
é interpretado como*(p++)
.p++
retornap
(uma cópia) e o valor armazenado no endereço anterior. Por que isso invocaria o UB? Está perfeitamente bem.++i
e a atribuição ai
. A segunda expressão não chama UB porque a expressãoi
não altera o valor dei
. No segundo exemplo,i++
é seguido por um ponto de sequência (,
) antes que o operador de atribuição seja chamado.Este é um acompanhamento da minha resposta anterior e contém material relacionado ao C ++ 11. .
Pré-requisitos : Um conhecimento elementar de Relações (Matemática).
É verdade que não há pontos de sequência no C ++ 11?
Sim! Isso é verdade.
Os pontos de sequência foram substituídos pelas relações Sequenced Before e Sequenced After (e Unsequenced e Indeterminately Sequenced ) no C ++ 11.
O que exatamente é essa coisa de 'Sequenciado antes'?
Sequenced Before (§1.9 / 13) é uma relação que é:
entre avaliações executadas por um único encadeamento e induz uma ordem parcial estrita 1
Formalmente significa que são dadas duas avaliações (veja abaixo)
A
eB
, seA
já tiver sido sequenciada antesB
, a execução deA
precederá a execução deB
. SeA
não é seqüenciado antesB
eB
não é seqüenciado antesA
, entãoA
eB
são unsequenced 2 .Avaliações
A
eB
são sequenciadas indeterminadamente quando umaA
é sequenciada antesB
ouB
é sequenciada antesA
, mas não é especificado qual 3 .[Notas]
1: Uma ordem estrita parcial é uma relação binária
"<"
ao longo de um conjuntoP
que éasymmetric
, etransitive
, ou seja, para todosa
,b
ec
emP
, temos que:........ (i). se a <b então ¬ (b <a) (
asymmetry
);........ ii) se a <b e b <c então a <c (
transitivity
).2: A execução de avaliações não subsequentes pode se sobrepor .
3: Avaliações seqüenciadas indeterminadamente não podem se sobrepor , mas podem ser executadas primeiro.
Qual é o significado da palavra 'avaliação' no contexto do C ++ 11?
No C ++ 11, a avaliação de uma expressão (ou subexpressão) em geral inclui:
cálculos de valores (incluindo a determinação da identidade de um objeto para avaliação de valor de valor e a busca de um valor previamente atribuído a um objeto para avaliação de valor de valor ) e
início de efeitos colaterais .
Agora (§1.9 / 14) diz:
Exemplo trivial:
int x;
x = 10;
++x;
O cálculo do valor e o efeito colateral associado a ele
++x
são sequenciados após o cálculo do valor e o efeito colateral dex = 10;
Portanto, deve haver alguma relação entre o comportamento indefinido e as coisas acima mencionadas, certo?
Sim! Direita.
Em (§1.9 / 15), foi mencionado que
Por exemplo :
+
operador não é sequencial entre si.<<
e>>
operadores não é sequencial entre si.4: Em uma expressão que é avaliada mais de uma vez durante a execução de um programa, unsequenced e indeterminadamente seqüenciado avaliações das suas subexpressões não precisam ser executadas de forma consistente em diferentes avaliações.
Isso significa no
x + y
cálculo do valor dex
ey
é sequenciado antes do cálculo do valor de(x + y)
.Mais importante
Exemplos:
i = i++ * ++i; // Undefined Behaviour
i = ++i + i++; // Undefined Behaviour
i = ++i + ++i; // Undefined Behaviour
i = v[i++]; // Undefined Behaviour
i = v[++i]: // Well-defined Behavior
i = i++ + 1; // Undefined Behaviour
i = ++i + 1; // Well-defined Behaviour
++++i; // Well-defined Behaviour
f(i = -1, i = -1); // Undefined Behaviour (see below)
Expressões
(5)
,(7)
e(8)
não invocar um comportamento indefinido. Confira as seguintes respostas para uma explicação mais detalhada.Nota final :
Se você encontrar alguma falha no post, deixe um comentário. Usuários avançados (Com rep> 20000), não hesite em editar a postagem para corrigir erros de digitação e outros erros.
fonte
f(i = -1, i = 1)
?++i
(sendo o valor avaliado antes do+
operador que o está usando), o padrão ainda não diz que seu efeito colateral deve ser concluído. Mas, de fato, porque retorna um ref para umlvalue
que éi
ele mesmo, DEVE ter finalizado o efeito colateral, pois a avaliação deve ser concluída, portanto, o valor deve estar atualizado. Essa era a parte louca para se entender.C ++ 17 (
N4659
) inclui uma proposta Refining Order de Avaliação de Expressão para Idiomatic C ++, que define uma ordem mais estrita de avaliação de expressão.Em particular, a seguinte frase
juntamente com os seguintes esclarecimentos
validar vários casos de comportamento anteriormente indefinido, incluindo o em questão:
No entanto, vários outros casos semelhantes ainda levam a um comportamento indefinido.
Em
N4140
:Mas em
N4659
Obviamente, o uso de um compilador compatível com C ++ 17 não significa necessariamente que se deva começar a escrever essas expressões.
fonte
i = i++ + 1;
comportamento é definido no c ++ 17, acho que mesmo que "O operando direito seja sequenciado antes do operando esquerdo", no entanto, a modificação para "i ++" e o efeito colateral da atribuição não são subsequentes, por favor, forneça mais detalhes para interpretá-losAcho que há uma razão fundamental para a mudança, não é apenas cosmético tornar a interpretação antiga mais clara: essa razão é simultaneidade. A ordem não especificada de elaboração é apenas a seleção de uma das várias ordens em série possíveis, isso é bem diferente das ordens antes e depois, porque, se não houver ordem especificada, é possível uma avaliação simultânea: não com as regras antigas. Por exemplo em:
anteriormente a então b ou b então a. Agora, aeb podem ser avaliados com instruções intercaladas ou mesmo em núcleos diferentes.
fonte
No
C99(ISO/IEC 9899:TC3)
qual parece ausente desta discussão até o momento, os seguintes steteents são feitos em relação à ordem de avaliação.fonte