Depois de ler esta resposta sobre comportamento indefinido e pontos de sequência, escrevi um pequeno programa:
#include <stdio.h>
int main(void) {
int i = 5;
i = (i, ++i, 1) + 1;
printf("%d\n", i);
return 0;
}
A saída é 2
. Oh Deus, eu não vi o decréscimo chegando! O que esta acontecendo aqui?
Além disso, ao compilar o código acima, recebi um aviso dizendo:
px.c: 5: 8: aviso: o operando esquerdo da expressão de vírgula não tem efeito
[-Wunused-value] i = (i, ++i, 1) + 1; ^
Por quê? Mas provavelmente será respondido automaticamente pela resposta da minha primeira pergunta.
c
operators
expression
compiler-warnings
comma-operator
gsamaras
fonte
fonte
printf("2\n");
Respostas:
Na expressão
(i, ++i, 1)
, a vírgula usada é o operador de vírgulaComo descarta seu primeiro operando, geralmente é útil apenas quando o primeiro operando tem efeitos colaterais desejáveis . Se o efeito colateral no primeiro operando não ocorrer, o compilador poderá gerar um aviso sobre a expressão sem efeito.
Portanto, na expressão acima, a extremidade esquerda
i
será avaliada e seu valor será descartado. Em seguida++i
, será avaliado e aumentarái
1 e novamente o valor da expressão++i
será descartado, mas o efeito colateral parai
é permanente . Então1
será avaliado e o valor da expressão será1
.É equivalente a
Observe que a expressão acima é perfeitamente válida e não invoca um comportamento indefinido porque há um ponto de sequência entre a avaliação dos operandos esquerdo e direito do operador de vírgula.
fonte
i
é inicializado com5
. Veja a declaraçãoint i = 5;
.++i
, essa expressão será avaliada,i
será incrementada e esse valor incrementado será o valor da expressão. No caso dei++
, essa expressão será avaliada, o valor antigo dei
será o valor da expressão,i
será incrementado a qualquer momento entre o ponto de sequência anterior e o próximo da expressão.Citando
C11
, capítulo6.5.17
, operador de vírgulaEntão, no seu caso,
é avaliado como
i
, é avaliado como uma expressão nula, valor descartado++i
, é avaliado como uma expressão nula, valor descartado1
valor retornado.Então, a declaração final parece
e
i
chega a2
. Acho que isso responde às duas perguntas,i
obtém um valor 2?Nota: Fwiw, como existe um ponto sequência presente após a avaliação do operando mão esquerda, uma expressão como
(i, ++i, 1)
não invocação UB, como uma pode geralmente pensar por engano.fonte
i
claramente não tem efeito! No entanto, não acho que fosse tão óbvio para um cara que não conhece o operador de vírgula (e não sabia como procurar ajuda, além de fazer uma pergunta). Pena que eu tenho tantos votos negativos! Vou verificar as outras respostas e depois decidir qual aceitar. Obrigado! Resposta superior agradável btw.Vamos analisá-lo passo a passo.
Então, obtemos 2. E a tarefa final agora:
O que quer que estivesse em i antes de ser substituído agora.
fonte
++i
isso não contribui para o resultado.int i = 0; for( ;(++i, i<max); )
O resultado de
é
Para
a avaliação ocorre de tal forma que o
,
operador descarta o valor avaliado e retém apenas o valor mais correto que é1
assim
fonte
Você encontrará boas leituras na página wiki do operador Vírgula .
Basicamente,
Isso significa que
por sua vez, avaliará
i
, descartará o resultado, avaliarái++
, descartará o resultado e, em seguida, avaliará e retornará1
.fonte
(void)exp; a= exp2;
enquanto eu só precisavaa = exp, exp2;
)Você precisa saber o que o operador de vírgula está fazendo aqui:
Sua expressão:
A primeira expressão
i
,, é avaliada, a segunda expressão++i
,, é avaliada e a terceira expressão1
, é retornada para toda a expressão.Assim, o resultado é:
i = 1 + 1
.Para sua pergunta de bônus, como você vê, a primeira expressão
i
não tem efeito algum, então o compilador reclama.fonte
A vírgula tem uma precedência "inversa". É isso que você obtém de livros antigos e manuais C da IBM (anos 70/80). Portanto, o último 'comando' é o que é usado na expressão pai.
No C moderno, seu uso é estranho, mas é muito interessante no antigo C (ANSI):
Embora todas as operações (funções) sejam chamadas da esquerda para a direita, apenas a última expressão será usada como resultado para 'while' condicional. Isso impede o manuseio de 'goto's' para manter um bloco de comandos exclusivo a ser executado antes da verificação da condição.
EDIT: Isso evita também uma chamada para uma função de manipulação que pode cuidar de toda a lógica nos operandos da esquerda e, portanto, retornar o resultado lógico. Lembre-se de que não tínhamos função embutida no passado de C. Portanto, isso poderia evitar uma sobrecarga de chamada.
fonte