Perché sizeof (x ++) non incrementa x?


505

Ecco il codice compilato in windows dev c ++:

#include <stdio.h>

int main() {
    int x = 5;
    printf("%d and ", sizeof(x++)); // note 1
    printf("%d\n", x); // note 2
    return 0;
}

Mi aspetto xdi essere 6 dopo aver eseguito la nota 1 . Tuttavia, l'output è:

4 and 5

Qualcuno può spiegare perché xnon si incrementa dopo la nota 1 ?


37
Vorrei notare che DevC ++ utilizza un compilatore obsoleto molto vecchio, potresti voler aggiornare a un IDE più recente, ad esempio Codeblocks Eclipse o Visual Studio
Tom J Nowell,

4
++ x produce lo stesso risultato di x ++, cygwin e gcc 3.4.4.
yehnan,

6
@Tim Pletzcker Perché il primo valore è la dimensione della variabile e non la variabile stessa.
Surfbutler,

La mia risposta è stata aggiunta a causa di una fusione con questa, la mia risposta mostra in realtà come è possibile ottenere una valutazione di runtime nel caso in VLAscui nessuno degli altri lo faccia.
Shafik Yaghmour,

2
questo tipo di scrittura dovrebbe essere evitato in quanto potrebbe causare effetti collaterali indesiderati. La tua domanda è già una di queste.
user666412

Risposte:


537

Dallo standard C99 (l'enfasi è mia)

6.5.3.4/2

L'operatore sizeof restituisce la dimensione (in byte) del suo operando, che può essere un'espressione o il nome tra parentesi di un tipo. La dimensione è determinata dal tipo di operando. Il risultato è un numero intero. Se il tipo di operando è un tipo di array di lunghezza variabile, viene valutato l'operando; in caso contrario, l'operando non viene valutato e il risultato è una costante intera.


51
"Se il tipo di operando è un tipo di array di lunghezza variabile, l'operando viene valutato" wow! Non me ne sono mai reso conto
Kos,

6
cosa intendi per tipo di array a lunghezza variabile? Ciò significa che l'operando è un array? Il codice in questo caso non è un array. Puoi chiarire le cose per me?
Neigyl R. Noval,

37
Un array di lunghezza variabile è un array dichiarato con la dimensione di un valore sconosciuto durante la compilazione, ad esempio se si legge Nda stdin e make int array[N]. Questa è una delle funzionalità di C99, non disponibile in C ++.
Kos,

21
@LegendofCage, in particolare ciò significherebbe che in qualcosa come sizeof(int[++x])(davvero, davvero una cattiva idea, comunque) si ++potrebbe valutare.
Jens Gustedt,

3
@ Joe Wreschnig: Si è valutato da gcc, clanged a ideone.com/Pf7iF
JFS

190

sizeofè un operatore in fase di compilazione, quindi al momento della compilazione sizeofe il suo operando vengono sostituiti dal valore del risultato. L' operando non viene valutato (tranne quando si tratta di un array di lunghezza variabile); conta solo il tipo di risultato.

short func(short x) {  // this function never gets called !!
   printf("%d", x);    // this print never happens
   return x;
}

int main() {
   printf("%d", sizeof(func(3))); // all that matters to sizeof is the 
                                  // return type of the function.
   return 0;
}

Produzione:

2

come shortoccupa 2 byte sulla mia macchina.

Modifica del tipo di ritorno della funzione in double:

double func(short x) {
// rest all same

darà 8come output.


12
Solo a volte - se possibile, è tempo di compilazione.
Martin Beckett,

10
-1, poiché ciò è in contraddizione con la risposta accettata (e corretta) e non cita lo standard.
Sam Hocevar,

1
C'è un bel vantaggio nella risoluzione in fase di compilazione dell'operatore sizeof () quando si lavora con stringhe. Se hai una stringa inizializzata come stringa tra virgolette, invece di usare strlen (), dove l'array di caratteri che comprende la stringa deve essere scansionato per il null-terminator in fase di esecuzione, sizeof (quoted_string) è noto al momento della compilazione, e quindi in fase di esecuzione. È una cosa piccola, ma se usi la stringa tra virgolette in un ciclo milioni e milioni di volte, fa una differenza significativa nelle prestazioni.
user2548100

Se lo usi davvero milioni e milioni di volte in un ciclo, non sarebbe molto più sensato fattorizzare il calcolo della lunghezza fuori dal ciclo? Spero sicuramente che tu non abbia milioni e milioni di diverse costanti codificate nel tuo codice. : -o
Veky

47

sizeof(foo) cerca davvero di scoprire la dimensione di un'espressione in fase di compilazione:

6.5.3.4:

L'operatore sizeof restituisce la dimensione (in byte) del suo operando, che può essere un'espressione o il nome tra parentesi di un tipo. La dimensione è determinata dal tipo di operando. Il risultato è un numero intero. Se il tipo di operando è un tipo di array di lunghezza variabile, viene valutato l'operando; in caso contrario, l'operando non viene valutato e il risultato è una costante intera.

In breve: array di lunghezza variabile, eseguiti in fase di esecuzione. (Nota: le matrici a lunghezza variabile sono una caratteristica specifica, non le matrici assegnate con malloc(3).) Altrimenti, viene calcolato solo il tipo di espressione e quello al momento della compilazione.


33

sizeofè un operatore incorporato in fase di compilazione e non è una funzione. Ciò diventa molto chiaro nei casi in cui è possibile utilizzarlo senza parentesi:

(sizeof x)  //this also works

1
Ma come è una risposta alla domanda?
Sebastian Mach,

5
@phresnel: questo per chiarire che sizeof è "strano" e non soggetto alle regole delle normali funzioni. Ho comunque modificato il post per rimuovere la possibile confusione con normali operatori di runtime come (+) e (-)
hugomg

L' sizeofoperatore non è un operatore in fase di compilazione, devi solo dargli un VLA per capirlo.
paxdiablo,

21

Nota

Questa risposta è stata unita da un duplicato, il che spiega la data tardiva.

Originale

Tranne che per le matrici di lunghezza variabile sizeof non valuta i suoi argomenti. Possiamo vedere questo dal progetto di sezione standard C99 6.5.3.4 La dimensione dell'operatore paragrafo 2 che dice:

L'operatore sizeof restituisce la dimensione (in byte) del suo operando, che può essere un'espressione o il nome tra parentesi di un tipo. La dimensione è determinata dal tipo di operando. Il risultato è un numero intero. Se il tipo di operando è un tipo di array di lunghezza variabile, viene valutato l'operando; in caso contrario, l'operando non viene valutato e il risultato è una costante intera.

Un commento ( ora rimosso ) ha chiesto se qualcosa di simile avrebbe valutato in fase di esecuzione:

sizeof( char[x++]  ) ;

e in effetti avrebbe funzionato anche qualcosa del genere ( vederli entrambi dal vivo ):

sizeof( char[func()]  ) ;

poiché sono entrambi array di lunghezza variabile. Anche se non vedo molto uso pratico in nessuno dei due.

Si noti che le matrici a lunghezza variabile sono trattate nel paragrafo 4 dei dichiaratori di array della sezione standard C99 :6.7.5.2

[...] Se la dimensione è un'espressione costante intera e il tipo di elemento ha una dimensione costante nota, il tipo di array non è un tipo di array a lunghezza variabile; in caso contrario, il tipo di array è un tipo di array a lunghezza variabile.

Aggiornare

In C11 la risposta cambia per il caso VLA, in alcuni casi non è specificato se l'espressione della dimensione sia valutata o meno. Dalla sezione 6.7.6.2 Dichiaratori di array che dice:

[...] Laddove un'espressione di dimensione fa parte dell'operando di un operatore sizeof e la modifica del valore dell'espressione di dimensione non influisce sul risultato dell'operatore, non è specificato se l'espressione di dimensione sia valutata o meno.

Ad esempio in un caso come questo ( vederlo dal vivo ):

sizeof( int (*)[x++] )

1
La cosa importante da ricordare qui è che la maggior parte delle volte sizeofè effettivamente una macro: non crea codice, ma pre-calcola il valore atteso e lo deposita direttamente nel codice. Si noti che questo è stato l' unico comportamento fino al C99, poiché i VBA non esistevano (non ne avevo mai sentito parlare fino a questa risposta, che ci crediate o no!)
Corley Brigman,

In che modo sizeof (char[x++]);utilizzerebbe il valore di xper qualcosa di diverso dalla determinazione del valore dell'espressione x++e del nuovo valore x, entrambi normali per quell'operatore?
supercat,

@alk ... err, sì, intendevo ovviamente "VLA" :) Shafik - perché dovrebbero valutare in fase di esecuzione? come ho detto, non ho mai visto VLA, ma i tipi di quelli sono entrambi noti al momento della compilazione, vero?
Corley Brigman,

@CorleyBrigman la risposta rapida sarebbe b / c lo dice lo standard, ma il motivo è b / c non conosciamo la dimensione dell'array in fase di compilazione, quindi dobbiamo valutare l'espressione in fase di esecuzione. I VLA sono un argomento interessante, qui ci sono due post che ho qui e qui .
Shafik Yaghmour,

@Shafik - ahh, ok, suppongo che la mia confusione fosse che non mi ero reso conto che char[x++]fosse un VLA. sembra efficace come char*ai miei occhi non conosciuti.
Corley Brigman,

11

Poiché l'operando sizeofdell'operatore non viene valutato, è possibile eseguire questa operazione:

int f(); //no definition, which means we cannot call it

int main(void) {
        printf("%d", sizeof(f()) );  //no linker error
        return 0;
}

Demo online: http://ideone.com/S8e2Y

Cioè, non è necessario definire la funzione fse viene utilizzata sizeofsolo in. Questa tecnica viene utilizzata principalmente nella metaprogrammazione di modelli C ++, poiché anche in C ++ l'operando di sizeofnon viene valutato.

Perché funziona? Funziona perché l' sizeofoperatore non opera sul valore , ma sul tipo dell'espressione. Quindi quando scrivi sizeof(f()), opera sul tipo dell'espressione f()e che non è altro che il tipo restituito della funzione f. Il tipo restituito è sempre lo stesso, indipendentemente dal valore che la funzione restituirebbe se fosse effettivamente eseguita.

In C ++, puoi anche questo:

struct A
{
  A(); //no definition, which means we cannot create instance!
  int f(); //no definition, which means we cannot call it
};

int main() {
        std::cout << sizeof(A().f())<< std::endl;
        return 0;
}

Eppure sembra che, in primo luogo sizeof, sto creando un'istanza di A, scrivendo A(), e quindi chiamando la funzione fsull'istanza, scrivendo A().f(), ma non succede nulla del genere.

Demo: http://ideone.com/egPMi

Ecco un altro argomento che spiega alcune altre proprietà interessanti di sizeof:


10

L'esecuzione non può avvenire durante la compilazione. Quindi ++i/ i++non accadrà. Inoltre sizeof(foo())non eseguirà la funzione ma restituirà il tipo corretto.


2
" L'esecuzione non può avvenire durante la compilazione. " Cosa intendi?
curioso

1
La compilazione creerà solo il codice oggetto ... Il codice oggetto verrà eseguito solo quando l'utente esegue il binario. Poiché sizeof accade al momento della compilazione, supponendo che i ++ aumenti sia errato.
Rakesh,

" Dato che sizeof accade al momento della compilazione " intendi: "com'è sizeofun'espressione della costante del tempo di compilazione"?
curioso

Come "#define" accade durante la pre-elaborazione, allo stesso modo sizeof accadrà al momento della compilazione. Durante la compilazione sono disponibili tutte le informazioni sul tipo, quindi viene valutato sizeof e lì durante la compilazione e il valore viene sostituito. Come già accennato in precedenza da @pmg "Dallo standard C99".
Rakesh,

1
" sizeof accadrà al momento della compilazione " per qualcosa che non è un array a lunghezza variabile
curioso

0

sizeofviene eseguito in fase di compilazione, ma x++può essere valutato solo in fase di esecuzione. Per risolvere questo, lo standard C ++ impone che l'operando di sizeofnon sia valutato. Lo standard C dice:

Se il tipo di operando [di sizeof] è un tipo di array di lunghezza variabile, viene valutato l'operando; in caso contrario, l'operando non viene valutato e il risultato è una costante intera.


Non esiste VLA in C ++.
LF

-2

sizeof() l'operatore fornisce solo la dimensione del tipo di dati, non valuta gli elementi interni.


Questo è falso, l' sizeof()operatore agisce in modo ricorsivo e otterrà la dimensione in byte di tutti gli elementi di un contenitore, membri di una classe o struttura, ecc. Puoi dimostrarlo facilmente creando una classe semplice con pochi membri e chiamandolo sizeof(). (Tuttavia, tutto ciò che è un puntatore lì dentro non può vedere la dimensione di - solo la dimensione del puntatore.) Ciò accade tutto in fase di compilazione, come affermato da altri commentatori: le espressioni all'interno di sizeof()non vengono valutate.
Tyler Shellberg,
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.