Warum kann dieser C-Code korrekt ausgeführt werden? [duplicate]

Lesezeit: 8 Minuten

Benutzer-Avatar
Yang. fr

Der C-Code sieht so aus:

 #include <stdio.h>
 #include <unistd.h> 
 #define DIM(a) (sizeof(a)/sizeof(a[0])) 
 struct obj
 {
     int a[1];
 };
 int main()
 {
     struct obj *p = NULL;
     printf("%d\n",DIM(p->a));
     return 0;
 }

Dieser Objektzeiger p ist NULLalso ich denke das p->a ist illegal. Aber ich habe diesen Code in Ubuntu14.04 getestet, er kann korrekt ausgeführt werden. Also ich will wissen warum…


Hinweis: Der ursprüngliche Code hatte int a[0] oben, aber ich habe das in geändert int a[1] da scheint jeder eher daran hängen zu bleiben als an dem tatsächlich Frage, nämlich:

Ist der Ausdruck sizeof(p->a) gültig wann p ist gleich NULL?

  • Nicht das, was hier passiert, aber gehen Sie nicht davon aus, dass “illegale” Dinge (undefiniertes Verhalten) nicht zu funktionieren scheinen. Sie können tatsächlich sehr lange funktionieren, bevor etwas so Einfaches wie ein Compiler-Versions-Upgrade sie kaputt macht.

    – Chris

    19. August 2015 um 2:34 Uhr

  • Chris Beck hat vollkommen recht: Ihr #define DIM() Makro ist Kompilierzeit. Es ist nur die Typ das zählt hier: nicht die tatsächlichen Laufzeitwerte.

    – paulsm4

    19. August 2015 um 2:45 Uhr

  • @paxdiablo es ist 2015, ist es nicht an der Zeit, C++ nicht mehr als Obermenge von C zu behandeln?

    – MM

    19. August 2015 um 3:09 Uhr

  • @paxdiablo dieser ganze Code ist gültiges C++ außer für int a[0]; was in beiden Sprachen ungültig ist. OP könnte einen C++-Compiler verwenden, wir wissen es nicht

    – MM

    19. August 2015 um 3:16 Uhr

  • @this: OMG, das ist urkomisch 🙂 Diese Frage hat eigentlich nichts mit Arrays der Länge Null zu tun, obwohl ich zugeben muss, dass es angesichts der Ablenkung in den Kommentaren so erscheinen mag. Die Frage ist nur, ob sizeof(p->a) gilt wann p == NULL. In Anbetracht der Verwirrung, die verursacht wird, denke ich, dass ich diese Null bearbeiten werde.

    – paxdiablo

    19. August 2015 um 6:25 Uhr

Benutzer-Avatar
Chris Beck

Da sizeof ist eine Konstruktion zur Kompilierzeit, es hängt nicht von der Auswertung der Eingabe ab. sizeof(p->a) wird basierend auf dem deklarierten Typ des Members ausgewertet p::a allein und wird zu einer Konstante in der ausführbaren Datei. Die Tatsache, dass p auf null zeigt, macht also keinen Unterschied.

Der Laufzeitwert von p spielt im Ausdruck überhaupt keine Rolle sizeof(p->a).

In C und C++, sizeof ist ein Operator und keine Funktion. Es kann entweder auf a angewendet werden Typ-ID oder ein Ausdruck. Außer in dem Fall, dass es sich um einen Ausdruck handelt und der Ausdruck ein Array variabler Länge ist (neu in C99) (wie von paxdiablo hervorgehoben), ist der Ausdruck ein unbewerteter Operand und das Ergebnis ist das gleiche, als ob Sie genommen hätten sizeof gegen die Typ dieses Ausdrucks stattdessen. (Vgl. C11-Referenzen aufgrund von Paxdiablo unten, C++14-Arbeitsentwurf 5.3.3.1)

  • Auch erwähnenswert sizeof ist ein Operator, keine Funktion. Könnte einige Verwirrung auf dem Weg lindern.

    – Qix – MONICA WURDE MISSHANDELT

    19. August 2015 um 3:06 Uhr

  • Arrays der Länge Null sind in Standard C illegal, aber in GNU C erlaubt (was ausdrücklich nicht dasselbe ist wie Standard C). Flexible Array-Mitglieder zählen nicht – die Dimension dort ist nicht 0, sondern unspezifiziert.

    – Jonathan Leffler

    19. August 2015 um 3:29 Uhr


  • Das ist sizeof(p) und sizeof((struct obj)) keinen unterschied haben? Was auch immer der Wert von p ist.

    – Jan. fr

    19. August 2015 um 4:40 Uhr


  • sizeof Kompilierzeit ist, es sei denn, Sie nehmen die Größe eines VLA-Typs.

    – Benutzer2357112

    19. August 2015 um 5:10 Uhr

  • @Yang.fr gut, sizeof(*p) und sizeof(obj) keinen Unterschied haben. sizeof(p) ist das gleiche wie sizeof(obj*)

    – Chris Beck

    19. August 2015 um 5:15 Uhr


Benutzer-Avatar
paxdiablo

Zunächst einmal, wenn Sie wirklich portierbaren Code wollen, sollten Sie nicht versuchen, ein Array der Größe Null zu erstellen1, wie Sie es in Ihrer ursprünglichen Frage getan haben, jetzt behoben. Aber da es nicht wirklich relevant für Ihre Frage ist, ob sizeof(p->a) gilt wann p == NULLkönnen wir vorerst ignorieren.

Ab Abschnitt C11 6.5.3.4 The sizeof and _Alignof operators (mein Fettdruck):

2/ Die sizeof Operator liefert die Größe (in Bytes) seines Operanden, der ein Ausdruck oder der Name eines Typs in Klammern sein kann. Das Die Größe wird vom Typ bestimmt des Operanden. Das Ergebnis ist eine ganze Zahl. Wenn der Typ des Operanden ein Array-Typ mit variabler Länge ist, wird der Operand ausgewertet; Andernfalls, der Operand wird nicht ausgewertet und das Ergebnis ist eine ganzzahlige Konstante.

Daher wird keine Auswertung des Operanden durchgeführt, es sei denn, es handelt sich um ein Array mit variabler Länge (was Ihr Beispiel nicht ist). Nur der Typ selbst wird verwendet, um die Größe herauszufinden.


1 Für die Sprachanwälte da draußen gibt C11 an 6.7.6.2 Array declarators (mein Fettdruck):

1/ Zusätzlich zu optionalen Typqualifizierern und dem Schlüsselwort staticdas [ and ] kann einen Ausdruck begrenzen oder *. Wenn sie einen Ausdruck begrenzen (der die Größe eines Arrays angibt), muss der Ausdruck einen ganzzahligen Typ haben. Wenn der Ausdruck ein konstanter Ausdruck ist, er muss einen Wert größer als Null haben.

Da das aber in der Einschränkungen Abschnitt (wo shall und shall not beinhalten kein undefiniertes Verhalten), es bedeutet einfach, dass das Programm selbst nicht streng konform ist. Es ist immer noch durch den Standard selbst abgedeckt.

  • Aber Arrays der Länge Null sind in Standard C illegal, obwohl GCC sie als Erweiterung erlaubt. Flexible Array-Mitglieder zählen nicht – die Dimension dort ist nicht 0, sondern unspezifiziert.

    – Jonathan Leffler

    19. August 2015 um 3:28 Uhr


  • @Jonathan, ich bin mir nicht sicher, ob das für die Frage selbst relevant ist, da Sie es beheben könnten, indem Sie einfach das drehen 0 in ein 1 und die Frage wäre immer noch gültig, da es darum geht, ob oder nicht sizeof(p->a) ist illegal, wenn p ist NULL. Trotzdem werde ich es der Vollständigkeit halber in die Antwort aufnehmen.

    – paxdiablo

    19. August 2015 um 3:31 Uhr

  • Die Strukturdeklaration ist illegal – egal, ob sie für irgendetwas verwendet wird oder nicht. Ein Programm, das diesen Strukturtyp enthält, ist nicht standardkonformes C.

    – Jonathan Leffler

    19. August 2015 um 3:32 Uhr

  • @Jonathan, da muss ich widersprechen, der Standard erlaubt Nichtkonformität. Es erwähnt ausdrücklich konforme, streng konforme und nicht konforme Programme und Implementierungen und erlaubt, dass die Implementierung konform ist, vorausgesetzt, dass ihre Erweiterungen das Verhalten von streng konformen Programmen nicht ändern. Die Aussage, dass der Kodex nicht streng konform ist, ist zwar richtig, aber das nicht bedeutet, dass es gemäß dem Standard illegales C ist.

    – paxdiablo

    19. August 2015 um 3:47 Uhr

  • @Yang.fr int x = rand() % 10 + 1; int p[x];

    – MM

    19. August 2015 um 5:07 Uhr

Benutzer-Avatar
MM

Dieser Code enthält eine Einschränkungsverletzung in ISO C aus folgendem Grund:

struct obj
{
    int a[0];
};

Arrays der Größe Null sind nirgendwo erlaubt. Daher definiert der C-Standard das Verhalten dieses Programms nicht (obwohl es scheint einige Diskussionen zu geben über das).

Der Code kann nur “korrekt ausgeführt werden”, wenn Ihr Compiler eine nicht standardmäßige Erweiterung implementiert, um Arrays der Größe Null zuzulassen.

Erweiterungen müssen dokumentiert werden (C11 4/8), also definiert hoffentlich die Dokumentation Ihres Compilers sein Verhalten für struct obj (eine Struktur der Größe Null?) und der Wert von sizeof p->aund ob oder nicht sizeof wertet seinen Operanden aus, wenn der Operand ein Array der Größe Null bezeichnet.

  • Beachten Sie, dass GCC ein Compiler ist, der eine nicht standardmäßige Erweiterung implementiert, um Arrays der Größe Null zuzulassen.

    – Jonathan Leffler

    19. August 2015 um 3:31 Uhr

  • Vielleicht möchten Sie (nachdem Sie die Kommentare zu anderen Fragen gelesen haben) den “illegalen” Teil abschwächen. Es ist nicht so sehr illegal, als dass es in einem streng konformen Programm ungültig ist. Implementierungen dürfen ausdrücklich Erweiterungen bereitstellen, sofern sie das Verhalten streng konformer Programme nicht ändern. Daher werden sie auch von ISO C abgedeckt. Und das bitbeschränkende Array der Größe Null befindet sich in einem Abschnitt mit Einschränkungen, der nichts mit UB zu tun hat.

    – paxdiablo

    19. August 2015 um 3:51 Uhr


  • @paxdiablo hat den Wortlaut geändert. In C++ sind Constraint-Verletzungen explizit UB; aber es sieht so aus, als hätten sich die C-Standard-Autoren entschieden, stattdessen vage zu sein …

    – MM

    19. August 2015 um 5:04 Uhr

  • Siehe meine Kommentare unter der Antwort von @paxdiablo. Der Code in der Frage erfordert eine Diagnose von einem konformen Compiler. Wenn der Compiler nicht dem Standard entspricht, dann hat der Standard keinen Einfluss auf den Compiler und er kann tun, was er will.

    – Jonathan Leffler

    19. August 2015 um 6:19 Uhr

  • @JonathanLeffler ja, aber was kann / muss der Compiler tun, nachdem er die Diagnose gegeben hat?

    – MM

    19. August 2015 um 6:59 Uhr

sizeof() kümmert sich nicht um den Inhalt von irgendetwas, es betrachtet lediglich den resultierenden Typ des Ausdrucks.

Seit C99 und variable length arrayswird es zur Laufzeit berechnet, wenn ein Array mit variabler Länge Teil des Ausdrucks in der ist sizeof operand.Andernfalls wird der Operand nicht ausgewertet und das Ergebnis ist ein integer constant

Zero-size array Erklärungen darin structs wurde nie von irgendjemandem erlaubt C standardaber einige ältere Compiler erlaubten es, bevor es für Compiler zum Standard wurde, es zuzulassen incomplete array declarations with empty brackets(flexible array members).

1352890cookie-checkWarum kann dieser C-Code korrekt ausgeführt werden? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy