Pergunta

Alguns dias atrás, houve uma discussão aqui sobre se a expressão

i = ++ i +1

Invoca UB (comportamento indefinido) ou não.

Finalmente, foi feita a conclusão de que ele invoca o UB, pois o valor de 'i' está mudando mais de uma vez entre dois pontos de sequência.

Eu estava envolvido em uma discussão com Johannes Schaub Nesse mesmo tópico. De acordo com ele

i = (i, i ++, i) +1 ------ (1) / * chama UB também * /

Eu disse (1) não invoca o UB porque os efeitos colaterais das subexpressões anteriores são limpos pelo operador de vírgula ', entre i e i ++ e entre i ++ e i.

Então ele deu a seguinte explicação:

"Sim, o ponto de sequência após o I ++ completa todos os efeitos colaterais antes dele, mas não há nada que interrompa o efeito colateral da atribuição que se sobrepõe ao efeito colateral do I ++.O problema subjacente é que o efeito colateral de uma tarefa não é especificado para acontecer após ou antes da avaliação de ambos os operandos da tarefa, e os pontos de sequência não podem fazer nada em relação à proteção disso: os pontos de sequência induzem uma ordem parcial: apenas porque porque há um ponto de sequência depois e antes que eu ++ não significa que todos os efeitos colaterais sejam sequenciados em relação a eu.

Além disso, observe que apenas um ponto de sequência não significa nada: a ordem das avaliações não é ditada pela forma de código. É ditado por regras semânticas. Nesse caso, não há regra semântica dizendo quando o efeito colateral da atribuição ocorre em relação à avaliação de seus operandos ou subexpressões desses operando ".

A declaração escrita em "Bold" me confundiu. Até onde sei:

"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 terá ocorrido".

Como os operadores de vírgula também especificam a ordem de execução que o efeito colateral do I ++ foi cancelado quando chegarmos à última I.He (Johannes) estaria certo se a ordem de avaliação não tivesse sido especificada (mas no caso do operador de vírgula, ela está bem especificada ).

Então, eu só quero saber se (1) invoca UB ou não?. Alguém pode dar outra explicação válida?

Obrigado!

Foi útil?

Solução

O padrão C diz isso sobre operadores de atribuição (C90 6.3.16 ou C99 6.5.16 Operadores de atribuição):

O efeito colateral da atualização do valor armazenado do operando esquerdo ocorre entre o ponto de sequência anterior e o próximo.

Parece -me que na declaração:

i=(i,i++,i)+1;

O ponto de sequência 'anterior' ao operador de atribuição seria o segundo operador de vírgula e o ponto de sequência 'próximo' seria o fim da expressão. Então, eu diria que a expressão não invoca comportamento indefinido.

No entanto, esta expressão:

*(some_ptr + i) = (i,i++,i)+1;

teria comportamento indefinido porque a ordem de avaliação dos 2 operandos do operador de atribuição é indefinida e, nesse caso, em vez do problema ser quando o efeito colateral do operador de atribuição ocorre, o problema é que você não sabe se o valor de Eu usei no operando da alça esquerda será avaliado antes ou depois do lado direito. Esse problema de ordem de avaliação não ocorre no primeiro exemplo, porque nessa expressão o valor de i não é realmente usado no lado esquerdo-tudo o que o operador de tarefas está interessado é o "lvalue-ness" de i.

Mas também acho que tudo isso é superficial (e minha compreensão das nuances envolvidas são superficiais o suficiente) para que eu não fique surpreso se alguém pudesse me convencer do contrário (em qualquer coisa).

Outras dicas

Eu acredito que a seguinte expressão definitivamente tem um comportamento indefinido.

i + ((i, i++, i) + 1)

O motivo é que o operador de vírgula especifica pontos de sequência entre as subexpressões entre parênteses, mas não especifica onde nessa sequência a avaliação do operando da mão esquerda de + ocorre. Uma possibilidade é entre os pontos de sequência em torno i++ E isso viola o 5/4 como i está escrito entre dois pontos de sequência, mas também é lido duas vezes entre os mesmos pontos de sequência e não apenas para determinar o valor a ser armazenado, mas também para determinar o valor do primeiro operando para o + operador.

Isso também tem comportamento indefinido.

i += (i, i++, i) + 1;

Agora, não tenho tanta certeza sobre esta afirmação.

i = (i, i++, i) + 1;

Embora os mesmos diretores se apliquem, i deve ser "avaliado" como um LValue modificável e pode ser feito a qualquer momento, mas não estou convencido de que seu valor é sempre ler Como parte disso. (Ou há outra restrição que a expressão viola para causar UB?)

A sub-expressão (i, i++, i) acontece como parte da determinação do valor a ser armazenado e essa subexpressão contém um ponto de sequência após o armazenamento de um valor para i. Não vejo como isso não exigiria o efeito colateral de i++ a ser concluído antes da determinação do valor a ser armazenado e, portanto, o ponto mais antigo possível de que o efeito colateral da atribuição poderia ocorrer.

Depois deste ponto de sequs iO valor é lido no máximo uma vez e apenas para determinar o valor que será armazenado de volta para i, então esta última parte está bem.

i=(i,i++,i)+1 ------ (1) /* invokes UB as well */

Não invoca comportamento indefinido. O efeito colateral de i++ ocorrerá antes da avaliação do próximo ponto de sequência, que é indicado pela vírgula seguindo -o e também antes da tarefa.

Bom idioma sudoku, no entanto. :-)

Editar: há uma explicação mais elaborada aqui.

Fiquei confuso no começo sobre a declaração de Johannes (LITB), mas ele mencionou que em:

i = (i, ++i, i) +1

<Hannes>
Se <a> é uma atribuição e é um incremento. :s: é um ponto de sequência, então os efeitos colaterais podem ser sequenciados da seguinte forma entre os pontos de sequência: (i :s: i++< a ><n> :s: i) + 1. O valor do escalar i foi alterado duas vezes entre o primeiro e o segundo ponto de sequência aqui. A ordem em que a atribuição e o incremento acontece não são especificados e, como entre eles, não há ponto de sequência, nem sequer é atômico em relação um ao outro. Isso é permitido que a ordem seja permitida pela ordem não especificada desses efeitos colaterais.

Isso é diferente de (i++, i++), como a ordem de avaliação das duas subexpressões é da esquerda para a direita e, no ponto de sequência entre eles, o incremento da avaliação anterior será concluído e o próximo incremento ainda não terá ocorrido. Isso aplica que não há mudança do valor de i entre dois pontos de sequência, o que faz (i++, i++) válido
</ Johannes>

Isso me fez pensar que a sequência mencionada por LITB é inválida porque, conforme C99:

6.5.16.1 (2) Na atribuição simples (=), o valor do operando direito é convertido para o tipo de expressão de atribuição e substitui o valor armazenado no objeto designado pelo operando esquerdo.

ou seja, o valor do operando direito precisa ser conhecido antes do efeito colateral da atribuição (modificação do valor armazenado no objeto correspondente ao operando esquerdo)

6.5.17 (2) O operando esquerdo de um operador de vírgula é avaliado como uma expressão vazia; Há um ponto de sequência após sua avaliação. Em seguida, o operando direito é avaliado; O resultado tem seu tipo e valor.

ou seja, o operando mais à direita da operação de vírgula precisa ser avaliado para conhecer o valor e o tipo da expressão de vírgula (e o valor do operando correto para o meu exemplo).

Portanto, neste caso, o 'ponto de sequência anterior' para o efeito colateral da atribuição seria, na verdade, a operação de vírgula mais à direita. A possível sequência mencionada por Johannes é inválida.

Por favor corrija-me se eu estiver errado.


Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top