Eu vi essa pergunta em um teste no qual temos que informar a saída do código a seguir.
#include<stdio.h>
int main(){
int k = 0;
while(+(+k--)!=0)
k=k++;
printf("%d\n", k);
return 0;
}
A saída é -1
. Não sei por que essa é a resposta.
O que a expressão +(+k--)
significa em C?
k=k++
é indefinido, mas não é indefinido porque nunca é executado por causa de uma condição ofuscada - estou votando para fechar como uma duplicata desta pergunta .Respostas:
[Para constar, editei esta resposta consideravelmente desde que ela foi aceita e votada. No entanto, ainda diz basicamente as mesmas coisas.]
Esse código é profundamente, talvez deliberadamente, confuso. Ele contém uma instância estritamente evitada do comportamento indefinido do pavor . É basicamente impossível determinar se a pessoa que construiu essa pergunta estava sendo muito, muito inteligente ou muito, muito estúpida. E a "lição" que esse código pretende ensinar ou questionar sobre você - a saber, que o operador unário plus não faz muito - certamente não é importante o suficiente para merecer esse tipo de desvio subversivo.
Existem dois aspectos confusos do código, a condição estranha:
e a declaração demente que controla:
Vou cobrir a segunda parte primeiro.
Se você tem uma variável como
k
essa que deseja incrementar em 1, C fornece não uma, não duas, três, mas quatro maneiras diferentes de fazer isso:k = k + 1
k += 1
++k
k++
Apesar dessa recompensa (ou talvez por causa disso), alguns programadores ficam confusos e expulsam contorções como
Se você não conseguir descobrir o que isso deve fazer, não se preocupe: ninguém pode. Essa expressão contém duas tentativas diferentes de alterar
k
o valor (ak =
parte e ak++
parte) e, como não há regra em C para dizer qual das modificações tentadas "vence", uma expressão como essa é formalmente indefinida , significando não apenas que ele tem nenhum significado definido, mas que todo o programa que o contém é suspeito.Agora, se você olhar com muito cuidado, verá que neste programa em particular, a linha
k = k++
não é realmente executada, porque (como estamos prestes a ver) a condição de controle é inicialmente falsa, então o loop é executado 0 vezes . Portanto, este programa em particular não pode realmente ser indefinido - mas ainda é patologicamente confuso.Veja também essas respostas canônicas de SO a todas as perguntas relacionadas a Comportamento indefinido desse tipo.
Mas você não perguntou sobre o
k=k++
papel. Você perguntou sobre a primeira parte confusa, a+(+k--)!=0
condição. Isso parece estranho, porque é estranho. Ninguém jamais escreveria esse código em um programa real. Portanto, não há razão para aprender a entender. (Sim, é verdade, explorar os limites de um sistema pode ajudá-lo a aprender sobre seus pontos positivos, mas há uma linha bastante clara em meu livro entre explorações imaginativas e instigantes e explorações abusivas e idiotas, e essa expressão está muito clara. o lado errado dessa linha.)Enfim, vamos examinar
+(+k--)!=0
. (E depois disso, vamos esquecer tudo.) Qualquer expressão como essa deve ser entendida de dentro para fora. Eu presumo que você sabe o quefaz. Ele pega
k
o valor atual e o "retorna" para o restante da expressão e diminui mais ou menos simultaneamentek
, ou seja, armazena a quantidadek-1
novamentek
.Mas então o que
+
faz? Isso é mais unário , e não binário. É como menos unário. Você sabe que binário menos subtrai: a expressãosubtrai b de a. E você sabe que menos unário nega as coisas: a expressão
dá a você o negativo de a. O
+
que é unário é ... basicamente nada.+a
fornecea
o valor de você , depois de alterar valores positivos para positivos e negativos para negativos. Então a expressãodá a você o que
k--
lhe deu, ou seja,k
o antigo valor.Mas não terminamos, porque temos
Isso apenas pega o que
+k--
lhe deu e se aplica unicamente+
a ele novamente. Então isso lhe dá o que+k--
lhe deu, o que lhek--
deu, que erak
o antigo valor.Então, no final, a condição
faz exatamente a mesma coisa que a condição muito mais comum
teria feito. (Ele também faz a mesma coisa que a condição de aparência ainda mais complicada
while(+(+(+(+k--)))!=0)
teria feito. E esses parênteses não são realmente necessários; ele também faz a mesma coisa quewhile(+ + + +k--!=0)
teria feito.)Mesmo descobrindo qual é a condição "normal"
faz é meio complicado. Há duas coisas acontecendo neste loop: Como o loop é executado potencialmente várias vezes, vamos:
k--
, para tornark
cada vez menor, mas tambémMas fazemos a
k--
parte imediatamente, antes (ou no processo de) decidir se devemos fazer outra viagem pelo circuito. E lembre-se de quek--
"retorna" o valor antigo dek
, antes de diminuí-lo. Nesse programa, o valor inicialk
é 0. Portanto,k--
"retornará" o valor antigo 0 e atualizek
para -1. Mas o restante da condição é!= 0
- mas, como acabamos de ver, na primeira vez que testamos a condição, obtivemos um 0. Portanto, não faremos nenhuma viagem pelo loop, portanto, não tentaremos executar o afirmação problemáticak=k++
.Em outras palavras, nesse loop específico, embora eu tenha dito que "existem duas coisas acontecendo", acontece que a coisa 1 acontece uma vez, mas a coisa 2 acontece zero vezes.
De qualquer forma, espero que agora esteja bem claro por que essa desculpa ruim para um programa acaba imprimindo -1 como o valor final de
k
. Normalmente, não gosto de responder a perguntas do questionário como esta - parece trapaça -, mas neste caso, como discordo muito de todo o objetivo do exercício, não me importo.fonte
À primeira vista, parece que esse código chama um comportamento indefinido, mas esse não é o caso.
Primeiro vamos formatar o código corretamente:
Então agora podemos ver que a declaração
k=k++;
está dentro do loop.Agora vamos rastrear o programa:
Quando a condição do loop é avaliada pela primeira vez,
k
possui o valor 0. A expressãok--
tem o valor atual dek
, que é 0, ek
é diminuído como efeito colateral. Então, após esta declaração, o valor dek
é -1.O líder
+
dessa expressão não tem efeito no valor, então+k--
avaliado como 0 e de forma semelhante+(+k--)
como 0.Então o
!=
operador é avaliado. Como0!=0
é falso, o corpo do loop não é inserido . Se o corpo tivesse sido inserido, você chamaria um comportamento indefinido porquek=k++
lê e gravak
sem um ponto de sequência. Mas o loop não é inserido, então não há UB.Finalmente, o valor de
k
é impresso, que é -1.fonte
if (x != NULL) *x = 42;
Isso é indefinido quandox == NULL
? Claro que não. O comportamento indefinido não ocorre em partes do código que não são executadas. A palavra comportamento é uma dica. Código que não é executado não tem comportamento, indefinido ou não.k=k++
é qualitativamente diferente de*x=42
. O último é bem definido sex
for um ponteiro válido, mas o primeiro é indefinido, não importa o quê. (Admito que você pode estar certo, mas, novamente, eu não vou discutir confinar, e eu estou cada vez mais preocupado temos sido magistralmente controlada.)Aqui está uma versão disso que mostra a precedência do operador:
Os dois
+
operadores unários não fazem nada, portanto essa expressão é exatamente equivalente ak--
. A pessoa que escreveu isso provavelmente estava tentando mexer com sua mente.fonte