Perché c = ++ (a + b) fornisce un errore di compilazione?


111

Dopo la ricerca, ho letto che l'operatore di incremento richiede che l'operando abbia un oggetto dati modificabile: https://en.wikipedia.org/wiki/Increment_and_decrement_operators .

Da questo immagino che dia un errore di compilazione perché (a+b)è un numero intero temporaneo e quindi non è modificabile.

Questa comprensione è corretta? Questa era la prima volta che cercavo di ricercare un problema, quindi se c'era qualcosa che avrei dovuto cercare, per favore avvisalo.


35
Non è male in termini di ricerca. Sei sulla strada giusta.
StoryTeller - Unslander Monica

35
Cosa ti aspetti che faccia l'espressione?
qrdl

4
secondo lo standard C11 6.5.3.1: L'operando dell'operatore di incremento o decremento prefisso deve essere di tipo reale o puntatore atomico, qualificato o non qualificato e deve essere un valore l modificabile
Christian Gibbons

10
Come vorresti che 1 fosse distribuito tra a e b? "Gli indici degli array dovrebbero iniziare da 0 o 1? Il mio compromesso di 0,5 è stato rifiutato senza, ho pensato, un'adeguata considerazione." - Stan Kelly-Bootle
Andrew Morton

5
Penso che una domanda da seguire sia perché dovresti mai farlo quando c = a + b + 1rende il tuo intento più chiaro ed è anche più breve da digitare. Gli operatori di incremento / decremento fanno due cose: 1. essi e il loro argomento formano un'espressione (che può essere usata, ad esempio, in un ciclo for), 2. modificano l'argomento. Nel tuo esempio stai usando la proprietà 1. ma non la proprietà 2., poiché butti via l'argomento modificato. Se non hai bisogno della proprietà 2. e desideri solo l'espressione, puoi semplicemente scrivere un'espressione, ad esempio x + 1 invece di x ++.
Trevor

Risposte:


117

È solo una regola, tutto qui, ed è forse lì per (1) rendere più facile scrivere compilatori C e (2) nessuno ha convinto il comitato per gli standard C a rilassarlo.

In modo informale puoi scrivere solo ++foose foopuò apparire sul lato sinistro di un'espressione di incarico come foo = bar. Dato che non puoi scrivere a + b = bar, non puoi nemmeno scrivere ++(a + b).

Non c'è una vera ragione per cui a + bnon si possa fornire un temporaneo su cui ++poter operare, e il risultato di ciò è il valore dell'espressione ++(a + b).


4
Penso che il punto (1) colpisca il chiodo sulla testa. Basta guardare le regole per la materializzazione temporanea in C ++ può far rivoltare lo stomaco (ma è potente, però, devo dirlo).
StoryTeller - Unslander Monica

4
@StoryTeller: Infatti, a differenza del nostro amato linguaggio C ++, il C compila ancora in assembly in modo relativamente banale.
Bathsheba

29
Ecco un vero motivo IMHO: sarebbe una terribile confusione se a ++volte avesse un effetto collaterale di modificare qualcosa ea volte semplicemente no.
aschepler

5
@dng: In effetti lo è; ecco perché sono stati introdotti i termini lvalue e rvalue, sebbene le cose siano più complicate di così oggigiorno (in particolare in C ++). Ad esempio, una costante non può mai essere un lvalue: qualcosa come 5 = a non ha senso.
Bathsheba

6
@Bathsheba Questo spiega perché 5 ++ causa anche un errore di compilazione
dng

40

Lo standard C11 afferma nella sezione 6.5.3.1

L'operando dell'operatore di incremento o decremento del prefisso deve essere di tipo reale o puntatore atomico, qualificato o non qualificato e deve essere un valore l

E "lvalore modificabile" è descritto nella sezione 6.3.2.1 sottosezione 1

Un lvalue è un'espressione (con un tipo di oggetto diverso da void) che potenzialmente designa un oggetto; se un lvalue non designa un oggetto quando viene valutato, il comportamento è indefinito. Quando si dice che un oggetto ha un tipo particolare, il tipo è specificato dal lvalue utilizzato per designare l'oggetto. Un lvalue modificabile è un lvalue che non ha un tipo di matrice, non ha un tipo incompleto, non ha un tipo qualificato const e se è una struttura o un'unione, non ha alcun membro (incluso, ricorsivamente, alcun membro o elemento di tutti gli aggregati o unioni contenuti) con un tipo qualificato const.

Quindi (a+b)non è un lvalue modificabile e non è quindi idoneo per l'operatore di incremento del prefisso.


1
La tua conclusione da queste definizioni è mancante ... Vuoi dire che (a + b) non designa potenzialmente un oggetto, ma questi paragrafi non lo consentono.
hkBst

21

Hai ragione. la ++cerca di assegnare il nuovo valore alla variabile originale. Quindi ++aprenderà il valore di a, lo aggiungerà 1e poi lo assegnerà di nuovo a a. Poiché, come hai detto, (a + b) è un valore temporaneo e non una variabile con un indirizzo di memoria assegnato, l'assegnazione non può essere eseguita.


12

Penso che per lo più tu abbia risposto alla tua stessa domanda. Potrei apportare una piccola modifica al tuo fraseggio e sostituire "variabile temporanea" con "rvalue" come menzionato da C.Gibbons.

I termini variabile, argomento, variabile temporanea e così via diventeranno più chiari man mano che imparerai a conoscere il modello di memoria di C (questa sembra una bella panoramica: https://www.geeksforgeeks.org/memory-layout-of-c-program/ ).

Il termine "rvalue" può sembrare opaco quando sei appena agli inizi, quindi spero che quanto segue aiuti a sviluppare un'intuizione al riguardo.

Lvalue / rvalue stanno parlando dei diversi lati di un segno di uguale (operatore di assegnazione): lvalue = lato sinistro (L minuscola, non "uno") rvalue = lato destro

Imparare un po 'su come C usa la memoria (e i registri) sarà utile per capire perché la distinzione è importante. A grandi linee , il compilatore crea un elenco di istruzioni in linguaggio macchina che calcolano il risultato di un'espressione (il rvalue) e poi inserisce quel risultato da qualche parte (il lvalue). Immagina un compilatore che si occupi del seguente frammento di codice:

x = y * 3

Nello pseudocodice di assemblaggio potrebbe assomigliare a questo esempio di giocattolo:

load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x

L'operatore ++ (e la sua controparte) necessita di un "da qualche parte" da modificare, essenzialmente tutto ciò che può funzionare come valore l.

Comprendere il modello di memoria C sarà utile perché avrai un'idea migliore nella tua testa su come gli argomenti vengono passati alle funzioni e (eventualmente) come lavorare con l'allocazione dinamica della memoria, come la funzione malloc (). Per ragioni simili potresti studiare qualche semplice programmazione in assembly ad un certo punto per avere un'idea migliore di ciò che sta facendo il compilatore. Inoltre, se stai usando gcc , l' opzione -S "Ferma dopo la fase di compilazione corretta; non assemblare". può essere interessante (anche se consiglierei di provarlo su un piccolo frammento di codice).

Per inciso: l'istruzione ++ esiste dal 1969 (sebbene sia iniziata nel predecessore di C, B):

L'osservazione di (Ken Thompson) (era) che la traduzione di ++ x era minore di quella di x = x + 1 ".

Seguendo questo riferimento su wikipedia, verrai portato a un interessante articolo di Dennis Ritchie (la "R" in "K&R C") sulla storia del linguaggio C, collegato qui per comodità: http://www.bell-labs.com/ usr / dmr / www / chist.html dove puoi cercare "++".


6

Il motivo è che lo standard richiede che l'operando sia un lvalue. L'espressione (a+b)non è un lvalue, quindi non è consentito applicare l'operatore di incremento.

Ora, si potrebbe dire "OK, questo è davvero il motivo, ma non v'è in realtà alcuna reale * * motivo diverso da quello" , ma purtroppo la particolare formulazione di come l'operatore lavora di fatto non richiedere che essere il caso.

L'espressione ++ E è equivalente a (E + = 1).

Ovviamente, non puoi scrivere E += 1se Enon è un lvalue. Il che è un peccato perché si potrebbe benissimo dire: "incrementa E di uno" e va fatto. In tal caso, applicare l'operatore su un valore non l sarebbe (in linea di principio) perfettamente possibile, a scapito di rendere il compilatore leggermente più complesso.

Ora, la definizione potrebbe essere banalmente riformulata (penso che non sia nemmeno originariamente C ma un cimelio di B), ma così facendo cambierebbe fondamentalmente la lingua in qualcosa che non è più compatibile con le sue versioni precedenti. Poiché il possibile beneficio è piuttosto piccolo ma le possibili implicazioni sono enormi, ciò non è mai accaduto e probabilmente non accadrà mai.

Se si considera C ++ oltre a C (la domanda è contrassegnata con C, ma si è discusso sui sovraccarichi di operatori), la storia diventa ancora più complicata. In C, è difficile immaginare che potrebbe essere così, ma in C ++ il risultato (a+b)potrebbe benissimo essere qualcosa che non è possibile incrementare affatto, o l'incremento potrebbe avere effetti collaterali molto considerevoli (non solo l'aggiunta di 1). Il compilatore deve essere in grado di affrontarlo e diagnosticare i casi problematici non appena si verificano. Su un lvalue, è ancora un po 'banale da controllare. Non così per qualsiasi tipo di espressione casuale all'interno di una parentesi che lanci alla poverina.
Questo non è un vero motivo per cui non potrebbe essere fatto, ma sicuramente dà una spiegazione al motivo per cui le persone che lo hanno implementato non sono esattamente entusiaste di aggiungere una tale caratteristica che promette pochissimi benefici a pochissime persone.



3

++ cerca di dare il valore alla variabile originale e poiché (a + b) è un valore temporaneo non può eseguire l'operazione. E sono fondamentalmente regole delle convenzioni di programmazione C per rendere la programmazione facile. Questo è tutto.


2

Quando viene eseguita l'espressione ++ (a + b), ad esempio:

int a, b;
a = 10;
b = 20;
/* NOTE :
 //step 1: expression need to solve first to perform ++ operation over operand
   ++ ( exp );
// in your case 
   ++ ( 10 + 20 );
// step 2: result of that inc by one 
   ++ ( 30 );
// here, you're applying ++ operator over constant value and it's invalid use of ++ operator 
*/
++(a+b);
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.