+ (+ k--) espressione in C


9

Ho visto questa domanda in un test in cui dobbiamo dire l'output del seguente codice.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

L'output è -1. Non sono sicuro del perché questa sia la risposta, però.

Cosa significa l'espressione +(+k--)in C?


4
È stato formattato in questo modo? Questo è cattivo ;-)
Peter - Ripristina Monica il

3
Suggerimento: non scrivere mai codice BS come questo nella vita reale.
Jabberwocky,

5
Questo non è UB. Vedi la mia risposta
dbush

@ Peter-ReinstateMonica Sì, è stato formattato in questo modo.
Ankur Gautam,

2
A causa della sua natura artificiosa e bizzarri colpi di scena - k=k++non è definito, ma non è definito perché non è mai stato eseguito a causa di una condizione offuscata - Sto votando per chiudere come duplicato di questa domanda .
Steve Summit,

Risposte:


10

[Per la cronaca, ho modificato questa risposta in modo molto significativo da quando è stata accettata e votata. Dice comunque sostanzialmente le stesse cose.]

Questo codice è profondamente, forse deliberatamente, confuso. Contiene un'istanza ristretta del comportamento terrificante non definito . È fondamentalmente impossibile determinare se la persona che ha costruito questa domanda fosse molto, molto intelligente o molto, molto stupida. E la "lezione" di questo codice potrebbe pretendere di insegnarti o interrogarti - vale a dire che l'operatore unario plus non fa molto - non è certamente abbastanza importante da meritare questo tipo di sovversione errata.

Esistono due aspetti confusi del codice, la strana condizione:

while(+(+k--)!=0)

e la dichiarazione demente che controlla:

k=k++;

Coprirò prima la seconda parte.

Se hai una variabile come kquella che vuoi incrementare di 1, C non ti dà uno, non due, non tre, ma quattro modi diversi per farlo:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

Nonostante questa generosità (o forse per questo), alcuni programmatori si confondono e tossiscono contorsioni come

k = k++;

Se non riesci a capire cosa dovrebbe fare, non preoccuparti: nessuno può. Questa espressione contiene due diversi tentativi di alterare kil valore (la k =parte e la k++parte) e poiché in C non esiste alcuna regola per dire quale delle tentate modifiche "vince", un'espressione come questa è formalmente indefinita , nel senso che non solo non ha un significato definito, ma l'intero programma che lo contiene è sospetto.

Ora, se guardi con molta attenzione, vedrai che in questo particolare programma, la linea k = k++non viene effettivamente eseguita, perché (come stiamo per vedere) la condizione di controllo è inizialmente falsa, quindi il ciclo viene eseguito 0 volte . Così questo particolare programma potrebbe non effettivamente essere indefinito - ma è ancora patologicamente confusione.

Vedi anche queste risposte SO canoniche a tutte le domande relative a comportamenti indefiniti di questo tipo.

Ma non hai chiesto la k=k++parte. Hai chiesto della prima parte confusa, la +(+k--)!=0condizione. Sembra strano, perché è strano. Nessuno scriverebbe mai un codice del genere in un vero programma. Quindi non c'è motivo di imparare a capirlo. (Sì, è vero, esplorare i confini di un sistema può aiutarti a conoscere i suoi punti positivi, ma c'è una linea abbastanza chiara nel mio libro tra esplorazioni fantasiose e stimolanti rispetto a esplorazioni sconclusionate e offensive, e questa espressione è molto chiara il lato sbagliato di quella linea.)

Comunque, esaminiamo +(+k--)!=0. (E dopo averlo fatto, dimentichiamoci di tutto.) Qualsiasi espressione come questa deve essere capita dall'interno. Presumo che tu sappia cosa

k--

lo fa. Prende kil valore corrente e lo "restituisce" al resto dell'espressione e contemporaneamente diminuisce più o meno k, ovvero immagazzina la quantità k-1in k.

Ma allora cosa fa +? Questo è un plus unario , non un plus binario. È proprio come meno unario. Sai che il meno binario fa sottrazione: l'espressione

a - b

sottrae b da a. E sai che meno unario nega le cose: l'espressione

-a

ti dà il negativo di a. Ciò che +fa unario è ... praticamente nulla. +ati dà avalore, dopo aver cambiato i valori positivi in ​​positivi e negativi in ​​negativi. Quindi l'espressione

+k--

ti dà tutto ciò che ti ha k--dato, cioè kil vecchio valore.

Ma non abbiamo finito, perché abbiamo

+(+k--)

Questo prende tutto ciò che +k--ti ha dato e si applica +di nuovo unario . Quindi ti dà tutto ciò che +k--ti ha dato, che era qualunque cosak-- ti abbia dato, che era kil vecchio valore.

Quindi, alla fine, la condizione

while(+(+k--)!=0)

fa esattamente la stessa cosa della condizione molto più ordinaria

while(k-- != 0)

avrebbe fatto. (Fa anche la stessa cosa della condizione ancora più complicatawhile(+(+(+(+k--)))!=0) avrebbe fatto la . E quelle parentesi non sono davvero necessarie; fa anche la stessa cosa che while(+ + + +k--!=0)avrebbe fatto.)

Anche capire quale sia la condizione "normale"

while(k-- != 0)

fa è un po 'complicato. Ci sono due cose che succedono in questo ciclo: Poiché il ciclo viene eseguito potenzialmente più volte, stiamo per:

  1. continuare a fare k-- , per renderlo sempre kpiù piccolo, ma anche
  2. continua a fare il corpo del ciclo, qualunque cosa faccia.

Ma facciamo subito la k--parte, prima (o in procinto di) decidere se fare un altro viaggio nel giro. E ricorda che k--"restituisce" il vecchio valore di k, prima di ridurlo. In questo programma, il valore iniziale di kè 0. Quindi k--"restituirà" il vecchio valore 0, quindi aggiorna ka -1. Ma poi il resto della condizione è != 0- ma come abbiamo appena visto, la prima volta che abbiamo testato la condizione, abbiamo ottenuto uno 0. Quindi non faremo alcun viaggio attraverso il ciclo, quindi non tenteremo di eseguire il dichiarazione problematica k=k++a tutti.

In altre parole, in questo particolare ciclo, anche se ho detto che "ci sono due cose che succedono", si scopre che la cosa 1 accade una volta, ma la cosa 2 accade zero volte.

Ad ogni modo, spero che ora sia sufficientemente chiaro il motivo per cui questa scusa scusa per un programma finisce per stampare -1 come valore finale di k. Normalmente, non mi piace rispondere a domande del quiz come questa - sembra imbrogliare - ma in questo caso, dal momento che sono così in disaccordo con tutto il punto dell'esercizio, non mi dispiace.


1
Gli esempi contrari sono tipici in qualsiasi classe di base. In quale altro modo si potrebbe scrivere una breve domanda sulla precedenza dell'operatore in un esame? Insegno matematica e se avessi un nickel ogni volta che uno studente mi chiedeva "Quando lo farò nella vita reale ?" ...
Scott

4
@Scott C'è inventato, e poi c'è inventato . Supponiamo che tu sia un istruttore di addestramento del conducente. Supponi di chiedere allo studente di guidare attraverso il traffico intenso del centro mentre lo picchia sulla testa con una mazza da baseball. Probabilmente, lo studente imparerà un'abilità potenzialmente utile. Ma io chiamo questo abuso. E sostengo la mia opinione che il codice in questa domanda sia offensivo, con valore pedagogico negativo.
Steve Summit,

1
"Sono d'accordo con la mia opinione" questo è giusto, ma la parola chiave qui è "opinione". Una risposta StackOverflow non è forse il luogo ottimale per esprimere le proprie opinioni. :)
Scott,

1
Per la cronaca, ho modificato questa risposta in modo piuttosto significativo da quando è stata accettata e votata. Dice comunque sostanzialmente le stesse cose, però.
Steve Summit,

11

A prima vista sembra che questo codice invoca comportamenti indefiniti, ma non è così.

Per prima cosa formattiamo il codice correttamente:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

Quindi ora possiamo vedere che l'istruzione k=k++;è all'interno del ciclo.

Ora tracciamo il programma:

Quando la condizione del ciclo viene valutata per la prima volta, kha il valore 0. L'espressione k--ha il valore corrente di k, che è 0, ed kè decrementata come effetto collaterale. Quindi dopo questa affermazione il valore di kè -1.

L'inizio +su questa espressione non ha alcun effetto sul valore, quindi +k--valutato su 0 e in modo simile+(+k--) modo su 0.

Quindi !=viene valutato l' operatore. Poiché 0!=0è falso, il corpo del loop non viene inserito . Se il corpo fosse stato inserito, invocheresti un comportamento indefinito perché k=k++entrambi legge e scrive ksenza un punto sequenza. Ma il ciclo non è inserito, quindi nessun UB.

kViene infine stampato il valore di che è -1.


1
È una domanda aperta, esistenziale, filosofica, imponderabile se il comportamento indefinito che non viene eseguito non è indefinito. Non è indefinito? Io non la penso così.
Steve Summit,

2
@SteveSummit Non c'è assolutamente nulla di esistenziale, filosofico, imponderabile al riguardo. if (x != NULL) *x = 42;Non è definito quando x == NULL? Ovviamente no. Il comportamento indefinito non si verifica in parti del codice che non vengono eseguite. La parola comportamento è un suggerimento. Il codice che non viene eseguito non ha alcun comportamento, non definito o altrimenti.
n. 'pronomi' m.

1
@SteveSummit Se un comportamento indefinito che non viene eseguito invalida l'intero programma, nessun programma C avrebbe un comportamento definito. Perché i programmi C sono sempre solo un ciclo / se condizione lontana dall'esecuzione di UB. Prendi una semplice iterazione di array per un esempio: itera fino a quando il controllo associato fallisce. Eseguire l'iterazione del ciclo successivo sarebbe un comportamento indefinito, ma poiché non è mai stato eseguito, va tutto bene.
cmaster - reinstalla monica il

3
Non ho intenzione di discuterne, perché non faccio avvocato di lingua. Ma scommetto che se ponessi questa domanda e la taggassi avvocato linguistico, potresti avviare un vivace dibattito. Si noti che k=k++è qualitativamente diverso da *x=42. Il secondo è ben definito se xè un puntatore valido, ma il primo non è definito, qualunque cosa accada. (Ammetto che potresti avere ragione, ma ancora una volta, non ho intenzione di discuterne, e sono sempre più preoccupato che siamo stati magistralmente trollati.)
Steve Summit

1
"Il secondo è ben definito se x è un puntatore valido, ma il primo è indefinito, qualunque cosa accada". Solo interi programmi possono avere un comportamento indefinito. Questa nozione non è applicabile ai frammenti di codice presi separatamente.
n. 'pronomi' m.

3

Ecco una versione di questo che mostra la precedenza dell'operatore:

+(+(k--))

I due +operatori unari non fanno nulla, quindi questa espressione è esattamente equivalente a k--. La persona che ha scritto questo molto probabilmente stava cercando di rovinare la tua mente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.