Warum erhöht sizeof (x ++) x nicht?


505

Hier ist der Code, der in Dev C ++ - Fenstern kompiliert wurde:

#include <stdio.h>

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

Ich erwarte x6 nach Ausführung von Note 1 . Die Ausgabe lautet jedoch:

4 and 5

Kann jemand erklären, warum xnicht nach Note 1 erhöht wird ?


37
Ich würde bemerken, dass DevC ++ einen sehr alten veralteten Compiler verwendet. Möglicherweise möchten Sie auf eine neuere IDE aktualisieren, z. B. Codeblocks Eclipse oder Visual Studio
Tom J Nowell,

4
++ x erzeugt das gleiche Ergebnis wie x ++, cygwin und gcc 3.4.4.
Yehnan

6
@ Tim Pletzcker Da der erste Wert die Größe der Variablen und nicht die Variable selbst ist.
Surfbutler

Meine Antwort wurde aufgrund einer Zusammenführung mit dieser hinzugefügt. Meine Antwort zeigt tatsächlich, wie Sie eine Laufzeitbewertung erhalten können, wenn VLAskeiner der anderen dies tut.
Shafik Yaghmour

2
Diese Art des Schreibens sollte vermieden werden, da dies unerwünschte Nebenwirkungen verursachen kann. Ihre Frage ist bereits eine davon.
user666412

Antworten:


537

Aus dem C99 Standard (der Schwerpunkt liegt bei mir)

6.5.3.4/2

Der Operator sizeof gibt die Größe (in Bytes) seines Operanden an, der ein Ausdruck oder der Name eines Typs in Klammern sein kann. Die Größe wird aus dem Typ des Operanden bestimmt. Das Ergebnis ist eine Ganzzahl. Wenn der Typ des Operanden ein Array-Typ variabler Länge ist, wird der Operand ausgewertet. Andernfalls wird der Operand nicht ausgewertet und das Ergebnis ist eine ganzzahlige Konstante.


51
"Wenn der Typ des Operanden ein Array-Typ variabler Länge ist, wird der Operand ausgewertet" wow! Ich habe das nie bemerkt
Kos

6
Was meinst du mit Array-Typ variabler Länge? Das heißt, der Operand ist ein Array? Der Code ist in diesem Fall kein Array. Können Sie die Dinge für mich klären?
Neigyl R. Noval

37
Ein Array mit variabler Länge ist ein Array, dessen Größe während der Kompilierung unbekannt ist, z. B. wenn Sie Naus stdin und make lesen int array[N]. Dies ist eine der C99-Funktionen, die in C ++ nicht verfügbar sind.
Kos

21
@LegendofCage, insbesondere würde dies bedeuten, dass in so etwas sizeof(int[++x])(wirklich, wirklich eine schlechte Idee, jedenfalls) das ++bewertet werden könnte.
Jens Gustedt

3
@ Joe Wreschnig: Es wird bewertet durch gcc, clangund bei ideone.com/Pf7iF
JFS

190

sizeofist ein Operator zur Kompilierungszeit , daher werden zum Zeitpunkt der Kompilierung sizeofund seines Operanden durch den Ergebniswert ersetzt. Der Operand wird überhaupt nicht ausgewertet (außer wenn es sich um ein Array mit variabler Länge handelt). Nur die Art des Ergebnisses ist von Bedeutung.

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;
}

Ausgabe:

2

as shortbelegt 2 Bytes auf meinem Computer.

Ändern des Rückgabetyps der Funktion in double:

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

wird 8als Ausgabe geben.


12
Nur manchmal - es ist Kompilierungszeit, wenn möglich.
Martin Beckett

10
-1, da dies im Widerspruch zur akzeptierten (und korrekten) Antwort steht und den Standard nicht zitiert.
Sam Hocevar

1
Die Auflösung des Operators sizeof () zur Kompilierungszeit bietet einen schönen Vorteil, wenn Sie mit Zeichenfolgen arbeiten. Wenn Sie eine Zeichenfolge haben, die als Zeichenfolge in Anführungszeichen initialisiert ist, anstatt strlen () zu verwenden, wobei das Zeichenarray, aus dem die Zeichenfolge besteht, zur Laufzeit nach dem Nullterminator durchsucht werden muss, ist sizeof (quote_string) zur Kompilierungszeit bekannt. und damit zur Laufzeit. Es ist eine kleine Sache, aber wenn Sie die angegebene Zeichenfolge millionenfach in einer Schleife verwenden, wirkt sich dies erheblich auf die Leistung aus.
user2548100

Wenn Sie es wirklich millionenfach in einer Schleife verwenden, wäre es nicht viel sinnvoller, die Längenberechnung aus der Schleife herauszurechnen? Ich hoffe sicherlich, dass Sie nicht Millionen und Abermillionen verschiedener fest codierter Konstanten in Ihrem Code haben. : -o
Veky

47

sizeof(foo) versucht wirklich, die Größe eines Ausdrucks beim Kompilieren zu ermitteln:

6.5.3.4:

Der Operator sizeof gibt die Größe (in Bytes) seines Operanden an, der ein Ausdruck oder der Name eines Typs in Klammern sein kann. Die Größe wird aus dem Typ des Operanden bestimmt. Das Ergebnis ist eine Ganzzahl. Wenn der Typ des Operanden ein Array-Typ variabler Länge ist, wird der Operand ausgewertet. Andernfalls wird der Operand nicht ausgewertet und das Ergebnis ist eine ganzzahlige Konstante.

Kurz gesagt: Arrays mit variabler Länge, die zur Laufzeit ausgeführt werden. (Hinweis: Arrays mit variabler Länge sind eine spezielle Funktion - keine Arrays, denen sie zugewiesen sind malloc(3).) Andernfalls wird nur der Typ des Ausdrucks berechnet, und zwar zur Kompilierungszeit.


33

sizeofist ein in die Kompilierungszeit eingebauter Operator und keine Funktion. Dies wird sehr deutlich, wenn Sie es ohne Klammern verwenden können:

(sizeof x)  //this also works

1
Aber wie ist das eine Antwort auf die Frage?
Sebastian Mach

5
@phresnel: Dies soll nur klarstellen, dass sizeof "seltsam" ist und nicht den Regeln normaler Funktionen unterliegt. Ich habe den Beitrag trotzdem bearbeitet, um die mögliche Verwechslung mit normalen Laufzeitoperatoren wie (+) und (-)
hugomg

Der sizeofOperator ist kein Operator zur Kompilierungszeit. Sie müssen ihm lediglich eine VLA geben, um dies herauszufinden.
paxdiablo

21

Hinweis

Diese Antwort wurde aus einem Duplikat zusammengeführt, das das späte Datum erklärt.

Original

Mit Ausnahme von Arrays mit variabler Länge wertet sizeof seine Argumente nicht aus. Wir können dies aus dem Entwurf des C99-Standardabschnitts 6.5.3.4 ersehen. Die Größe des Operators Absatz 2, der besagt:

Der Operator sizeof gibt die Größe (in Bytes) seines Operanden an, der ein Ausdruck oder der Name eines Typs in Klammern sein kann. Die Größe wird aus dem Typ des Operanden bestimmt. Das Ergebnis ist eine Ganzzahl. Wenn der Typ des Operanden ein Array-Typ variabler Länge ist, wird der Operand ausgewertet. Andernfalls wird der Operand nicht ausgewertet und das Ergebnis ist eine ganzzahlige Konstante.

In einem Kommentar ( jetzt entfernt ) wurde gefragt, ob so etwas zur Laufzeit ausgewertet werden würde:

sizeof( char[x++]  ) ;

und in der Tat würde so etwas auch funktionieren ( beide live sehen ):

sizeof( char[func()]  ) ;

da sie beide Arrays variabler Länge sind. Obwohl ich in beiden Fällen nicht viel praktischen Nutzen sehe.

Beachten Sie , variabler Länge Arrays werden in der überdachten Entwurf C99 - Standard Abschnitt 6.7.5.2 Array Deklaratoren Absatz 4 :

[...] Wenn die Größe ein ganzzahliger konstanter Ausdruck ist und der Elementtyp eine bekannte konstante Größe hat, ist der Array-Typ kein Array-Typ variabler Länge. Andernfalls ist der Array-Typ ein Array-Typ mit variabler Länge.

Aktualisieren

In C11 ändert sich die Antwort für den VLA-Fall. In bestimmten Fällen ist nicht angegeben, ob der Größenausdruck ausgewertet wird oder nicht. Aus dem Abschnitt 6.7.6.2 Array-Deklaratoren, der besagt:

[...] Wenn ein Größenausdruck Teil des Operanden eines Operators sizeof ist und das Ändern des Werts des Größenausdrucks das Ergebnis des Operators nicht beeinflusst, ist nicht angegeben, ob der Größenausdruck ausgewertet wird oder nicht.

Zum Beispiel in einem Fall wie diesem ( live sehen ):

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

1
Das Wichtigste dabei ist, dass es sich meistens um sizeofein Makro handelt - es erstellt keinen Code, sondern berechnet den erwarteten Wert vor und legt ihn direkt im Code ab. Beachten Sie, dass dies das einzige Verhalten bis C99 war, da es keine VBAs gab (ich hatte bis zu dieser Antwort noch nie davon gehört, ob Sie es glauben oder nicht!)
Corley Brigman,

Inwiefern würde sizeof (char[x++]);der Wert von xfür etwas anderes verwendet, als den Wert des Ausdrucks x++und den neuen Wert für zu bestimmen x, die beide bei diesem Operator normal sind?
Supercat

@alk ... ähm, ja, ich meinte natürlich 'VLA' :) Shafik - warum sollten diese zur Laufzeit auswerten? Wie gesagt, ich habe noch nie VLAs gesehen, aber die Typen dieser beiden sind zum Zeitpunkt der Kompilierung bekannt, nicht wahr?
Corley Brigman

@CorleyBrigman Die schnelle Antwort wäre b / c, wie der Standard sagt, aber der Grund ist b / c. Wir kennen die Arraygröße zur Kompilierungszeit nicht, daher müssen wir den Ausdruck zur Laufzeit auswerten. VLAs sind ein interessantes Thema, hier sind zwei Beiträge, die ich hier und hier dazu habe .
Shafik Yaghmour

@Shafik - ahh, ok, ich glaube meine Verwirrung war, dass ich nicht wusste, dass char[x++]es sich um eine VLA handelt. es sieht char*für meine unbekannten Augen effektiv aus wie ein .
Corley Brigman

11

Da der Operand des sizeofOperators nicht ausgewertet wird, können Sie Folgendes tun:

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

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

Online-Demo: http://ideone.com/S8e2Y

Das heißt, Sie müssen die Funktion nicht definieren, fwenn sie sizeofnur in verwendet wird. Diese Technik wird hauptsächlich bei der Metaprogrammierung von C ++ - Vorlagen verwendet, da selbst in C ++ der Operand von sizeofnicht ausgewertet wird.

Warum funktioniert das? Es funktioniert, weil der sizeofOperator nicht mit dem Wert arbeitet , sondern mit dem Typ des Ausdrucks. Wenn Sie also schreiben sizeof(f()), arbeitet es mit dem Typ des Ausdrucks f(), der nichts anderes als der Rückgabetyp der Funktion ist f. Der Rückgabetyp ist immer derselbe, unabhängig davon, welchen Wert die Funktion zurückgeben würde, wenn sie tatsächlich ausgeführt wird.

In C ++ können Sie sogar Folgendes tun:

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;
}

Es sieht jedoch so aus sizeof, als würde ich zuerst eine Instanz von erstellen A, indem ich schreibe A()und dann die Funktion ffür die Instanz aufrufe, indem ich schreibe A().f(), aber so etwas passiert nicht.

Demo: http://ideone.com/egPMi

Hier ist ein weiteres Thema, das einige andere interessante Eigenschaften von erklärt sizeof:


10

Die Ausführung kann während der Kompilierung nicht erfolgen. Also ++i/ i++wird nicht passieren. Auch sizeof(foo())nicht die Funktion ausführen , aber richtige Art zurück.


2
" Die Ausführung kann nicht während der Kompilierung erfolgen. " Was meinst du?
Neugieriger

1
Beim Kompilieren wird nur Objektcode erstellt ... Der Objektcode wird nur ausgeführt, wenn der Benutzer die Binärdatei ausführt. Da sizeof zur Kompilierungszeit auftritt, ist die Annahme, dass das Inkrementieren von i ++ falsch ist, falsch.
Rakesh

" Wie sizeof zur Kompilierungszeit passiert " meinst du: "wie sizeofist ein konstanter Ausdruck zur Kompilierungszeit"?
Neugieriger

So wie "#define" während der Vorverarbeitung auftritt, wird auch sizeof zur Kompilierungszeit ausgeführt. Während der Kompilierung sind alle Typinformationen verfügbar, sodass die Größe von dann und dort während der Kompilierung ausgewertet und der Wert ersetzt wird. Wie bereits von @pmg "From the C99 Standard" erwähnt.
Rakesh

1
" sizeof wird zur Kompilierungszeit passieren " für etwas, das kein Array mit variabler Länge ist
neugieriger Kerl

0

sizeofwird zur Kompilierungszeit ausgeführt, x++kann jedoch nur zur Laufzeit ausgewertet werden. Um dies zu lösen, schreibt der C ++ - Standard vor, dass der Operand von sizeofnicht ausgewertet wird. Der C-Standard sagt:

Wenn der Typ des Operanden [von sizeof] ein Array-Typ variabler Länge ist, wird der Operand ausgewertet. Andernfalls wird der Operand nicht ausgewertet und das Ergebnis ist eine ganzzahlige Konstante.


In C ++ gibt es keine VLA.
LF

-2

sizeof() Der Operator gibt nur die Größe des Datentyps an und wertet keine inneren Elemente aus.


Dies ist falsch, der sizeof()Operator handelt rekursiv und erhält die Größe aller Elemente eines Containers, der Mitglieder einer Klasse oder Struktur usw. in Byte. Sie können dies sehr einfach selbst beweisen, indem Sie eine einfache Klasse mit wenigen Mitgliedern und erstellen aufrufen sizeof(). (Alles dort, was ein Zeiger ist, kann jedoch nicht die Größe von - nur die Größe des Zeigers - sehen.) Dies geschieht alles zur Kompilierungszeit, wie andere Kommentatoren angegeben haben: Ausdrücke innerhalb des Zeigers werden sizeof()nicht ausgewertet.
Tyler Shellberg
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.