Poiché questa domanda è stata posta a causa di una discussione commento a base here, fornirò qualche contesto:
primo commento: L'ordine delle operazioni è non garantito per essere l'ordine in che si passano gli argomenti alla funzione. Alcune persone (erroneamente) presumono che gli argomenti saranno valutati da destra a sinistra, ma secondo lo standard, il comportamento non è definito.
Il PO accetta e comprende questo. Non ha senso ripetere che il your_function(++i, ++i)
è UB.
In risposta a quel commento: Grazie per il tuo commento vedo che printf
possono essere valutati in qualsiasi ordine, ma ho capito che per essere a causa printf
argomenti sono parte di un va_list
. Stai dicendo che gli argomenti di qualsiasi funzione sono eseguiti in un ordine arbitrario?
OP chiedere chiarimenti, così ho elaborato un po ':
Seconda commento: Sì, questo è esattamente quello che sto dicendo. anche chiamando lo int your_function(int a, int b) { return a - b; }
non garantisce che le espressioni passate vengano valutate da sinistra a destra.Non esiste un punto di sequenza (un punto in cui vengono eseguiti tutti gli effetti collaterali delle precedenti valutazioni). Prendi lo this example. La chiamata nidificata è un punto di sequenza, quindi la chiamata esterna passa i+1
(13) e il valore di ritorno della chiamata interna (indefinita, in questo caso -1 perché i++
, i
valuta a 12, 13 a quanto pare), ma non c'è garantire che questo sia sempre il caso
Questo ha reso abbastanza chiaro che questi tipi di costrutti attivano UB per tutte le funzioni.
Wikipedia confusione
quotazioni op Questa:
Dopo l'azione associata formato di conversione ingresso/uscita specificatore. Ad esempio, nel printf espressione ("foo% n% d", & a, 42), v'è un punto sequenza dopo l'% n viene valutata prima della stampa 42.
viene quindi applicato al suo frammento (prinf("%d - %d - %d\n", i, your_function(++i, ++i), i);
) aspettando che gli specificatori di formato servano come punti di sequenza.
A cosa viene riferito dicendo "" identificatore del formato di conversione input/output " è l'identificatore %n
. L'argomento corrispondente deve essere essere un puntatore a un numero intero senza segno e verrà assegnato il numero di caratteri stampati fino a quel momento. Naturalmente, è necessario valutare %n
prima di stampare il resto degli argomenti. Tuttavia, utilizzando il puntatore passato per %n
in altri argomenti è ancora pericoloso: è non UB (beh, non lo è, ma può essere):
printf("Foo %n %*s\n", &a, 100-a, "Bar");//DANGER!!
C'è un punto sequenza prima viene chiamata la funzione, quindi l'espressione 100-a
sarà prima di %n
ha impostato &a
sul valore corretto. Se a
non è inizializzato, allora 100-a
è UB. Se a
viene inizializzato su 0, ad esempio, il risultato dell'espressione sarà pari a 100. Nel complesso, tuttavia, questo tipo di codice richiede praticamente problemi. Trattarlo come pessima pratica, o peggio ...
Basta guardare l'output generato da una di queste affermazioni:
unsigned int a = 90;
printf("%u %n %*s\n",a, &a, 10, "Bar");//90 Bar
printf("%u\n", a);//3
printf("Foo %u %n %*s\n",a, &a, 10-a, "Bar");//Foo 3 Bar < padding used: 10 - 3, not 10 - 6
printf("%u\n", a);//6
a come si può vedere, n
ottiene riassegnato all'interno di printf
, quindi non puoi usare il suo nuovo valore nella lista degli argomenti (perché c'è un punto di sequenza). Se si prevede di riassegnare "n
" sul posto ", in sostanza si aspetta che C salti fuori dalla chiamata della funzione, valuti altri argomenti e torni indietro nella chiamata. Questo non è possibile. Se si dovesse cambiare unsigned int a = 90;
a unsigned int a;
, il comportamento non è definito.
Per quanto riguarda 's il 12
Ora, poiché l'OP leggere su punti di sequenza, si accorge, giustamente, che questa dichiarazione:
printf("%d - %d - %d\n", i, your_function(++i, ++i), i);
è leggermente diversa: your_function(++i, ++i)
è un punto di sequenza e garanzie che i
verrà incrementato due volte. Questa chiamata di funzione è un punto di sequenza perché:
Prima di immettere una funzione in una chiamata di funzione. L'ordine in cui vengono valutati gli argomenti non è specificato, ma questo punto sequenza significa che tutti i loro effetti collaterali sono completi prima di inserire la funzione
Ciò significa che, prima printf
chiamata, your_function
ha a essere chiamato (poiché il suo valore restituito è uno degli argomenti per la chiamata printf
) e i
verrà incrementato due volte.
Questo potrebbe spiegare l'uscita essendo "12-0 - 12", ma è garantito per essere l'uscita?
No
Tecnicamente, anche se la maggior parte dei compilatori valuteranno la chiamata your_function(++i, ++i);
prima, lo standard permetterebbe un compilatore di valutare gli argomenti passati al sprintf
da sinistra a destra (l'ordine non è specificato, dopo tutto). Quindi questo sarebbe un risultato altrettanto valida:
10 - 0 - 12
//or even
12 - 0 - 10
//and
10 - 0 - 10
//technically, even this would be valid
12 - 0 - 11
Sebbene quest'ultima uscita è estremamente improbabile (sarebbe molto inefficiente)
Secondo [questo] (http://stackoverflow.com/questions/ 4176328/undefined-behavior-and-sequence-points) è un comportamento non definito. Credo anche che 'your_function (++ i, ++ i)' sia anch'esso un comportamento indefinito. – NathanOliver
'your_function (++ i, ++ i)' è chiaramente UB. –
I punti di sequenza descritti dalla virgola standard rientrano nel corpo della funzione 'printf()' dopo che è stato chiamato. Hai un estremo comportamento indefinito nella sequenza chiamante prima che venga chiamata la funzione 'printf()' - il che significa che qualsiasi risultato è accettabile (incluso quello che hai ottenuto). –