Array variabler Länge in der Mitte der Struktur – warum dieser C-Code für gcc gültig ist

Lesezeit: 7 Minuten

Benutzer-Avatar
osgx

Es gibt einen seltsamen Code, der VLA (Variable Length Arrays) verwendet, der von gcc 4.6 als gültiges C (C99, C11) behandelt wird:

$ cat a.c
int main(int argc,char**argv)
{
  struct args_t{
     int a;
     int params[argc];        // << Wat?
                        // VLA in the middle of some struct, between other fields
     int b;
  } args;

  args.b=0;

  for(args.a=0;args.a<argc;args.a++)
  {
    args.params[args.a]=argv[0][0];
    args.b++;
  }
  return args.b;
}

Dieser Code wurde ohne Warnungen kompiliert:

$ gcc-4.6 -Wall -std=c99 a.c && echo $?
0
$ ./a.out ; echo $?
1
$ ./a.out 2; echo $?
2
$ ./a.out 2 3; echo $?
3

Das gleiche für -std=c1x:

$ gcc-4.6 -Wall -std=c1x a.c && echo $?
0

Aber das funktioniert nicht mit Intel C Compiler oder mit Clang+LLVM:

$ icc a.c -o a.icc
a.c(5): warning #1361: variable-length array field type will be treated as zero-length array field type
       int params[argc];
                  ^
$ ./a.icc; echo $?
47

$ clang a.c -o a.clang
a.c:5:10: error: fields must have a constant size: 'variable length array in structure' extension will never be supported
     int params[argc];
         ^
1 error generated.

So:

  1. Warum wird dies von GCC als gültig angesehen?
  2. Wenn es sich um eine Erweiterung von GCC handelt, wo wird es beschrieben?
  3. Ist es in den ISO-Normen C99 und C11 gültig?

  • +1; in einer kürzlich gestellten Frage hat jemand ein tatsächliches Programm gepostet, das diese Funktion ausnutzt. Ich war überrascht, dass es funktionierte.

    – Fred Foo

    31. Januar 2013 um 15:44 Uhr

  • Dokumentiert in GCC in “6.19 Arrays of Variable Length” in ein Satz und ein Beispiel nur: “Als Erweiterung akzeptiert GCC Arrays variabler Länge als Mitglied einer Struktur oder einer Vereinigung. void foo(int n) { struct S { int x[n]; }; }“. Dokumentation aktualisiert in gc 4.9: gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Variable-Length.html github.com/gcc-mirror/gcc/commit/… (svn 208836) PR c/37428 am 26.03.2014 von Marek Polacek von RedHat; gcc.gnu.org/bugzilla/show_bug.cgi?id=37428 “GNU VLA-in-Structure-Erweiterung ist undokumentiert” (2008-09)

    – osgx

    6. März 2017 um 19:38 Uhr


Benutzer-Avatar
Lundin

GCC erlaubt es nicht, mit zu kompilieren -std=c99 -pedantic-errors. Ein VLA innerhalb einer Struktur ist anscheinend ein (schlecht dokumentiertes) nicht standardmäßiges GNU C-Feature. Sieh dir das an.

  • Wer jemals verwendet -pedantic-errors? Warum ist diese Erweiterung so tief versteckt? Kein Benutzer von gcc wird wissen, dass dieser Code falsch ist, da es im standardmäßigen gcc-Modus keine Warnung gibt.

    – osgx

    31. Januar 2013 um 15:51 Uhr

  • @osgx: -pedantic ist das zu verwendende Flag, wenn Sie Ihren Code streng nach dem Standard kompilieren möchten. Und die Flagge ist ein ziemlich bekanntes und dokumentiertes Merkmal.

    – Alok Speichern

    31. Januar 2013 um 15:54 Uhr

  • @osgx Einer der größten Fehler bei GCC ist in der Tat, dass es standardmäßig nicht auf Standard-C, sondern auf “GNU goo” setzt. Wenn es um Konformität und Portabilität geht, sollten Sie immer mit -pedantic und -Wall kompilieren. Ich habe es nicht versucht, aber -Wall könnte sich vielleicht auch über die VLA beschweren.

    – Ludin

    31. Januar 2013 um 16:03 Uhr

  • @osgx: -pedantic ist bekannt; es veranlasst gcc, zumindest vor Verstößen gegen den Standard zu warnen. -pedantic-errors ist vielleicht weniger bekannt; es ist wie -pedantic, außer dass Verstöße als schwerwiegende Fehler behandelt werden. Eines der wichtigsten Dinge, die Sie über gcc wissen sollten, ist, dass es standardmäßig kein standardkonformer C-Compiler ist. gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Standards.html#Standards

    – Keith Thompson

    31. Januar 2013 um 16:17 Uhr

  • Leider lehnt -pedantic auch einige universell unterstützte optionale C-Funktionen wie Bitfelder mit Typen ab, die größer als int sind.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    31. Januar 2013 um 16:24 Uhr

Der Standard ist ziemlich klar, dass VLAs in a nicht erlaubt sind struct:

6.7.2.1 Struktur- und Vereinigungsspezifizierer

9 – Ein Mitglied einer Struktur oder Vereinigung kann jeden vollständigen Objekttyp außer einem variabel modifizierten Typ haben. […]

Variabel modifizierte Typen sind (wie zu erwarten) solche, die von einem Array variabler Länge abgeleitet sind (z. B. durch Hinzufügen von Array-Dimensionen oder cv-Qualifizierern):

6.7.6 Deklaratoren

3 – […] Wenn es in der verschachtelten Folge von Deklaratoren in einem vollständigen Deklarator einen Deklarator gibt, der einen Arraytyp mit variabler Länge angibt, wird der vom vollständigen Deklarator angegebene Typ als variabel modifiziert bezeichnet. Darüber hinaus ist jeder Typ, der durch Deklaratortypableitung von einem variabel modifizierten Typ abgeleitet wird, selbst variabel modifiziert.

  • In C99 sind flexible Arrays als letztes Mitglied einer Struktur zugelassen.

    – Etienne

    3. Dezember 2013 um 12:37 Uhr


  • @Étienne Flexible Arrays sind keine VLAs.

    – ekatmur

    3. Dezember 2013 um 13:33 Uhr

Die Autoren des C89-Standards erkannten, dass viele Implementierungen nützliche Funktionen implementierten, die bei anderen Implementierungen unpraktisch sein könnten, und erkannten dies als eine gute Sache. Der Standard war als Mindestanforderung für Implementierungen gedacht; Es war nie beabsichtigt, Implementierungen davon abzuhalten, darüber hinausgehende Funktionen bereitzustellen.

Der Standard verlangt, dass, wenn eine konforme Implementierung die Deklaration eines Arrays variabler Länge innerhalb einer im Blockbereich definierten Struktur zulässt, dieses Verhalten entweder als Erweiterung dokumentiert oder eine Diagnose ausgegeben werden muss, wenn Code eine solche Deklaration enthält. Da eine Implementierung den Code beliebig verarbeiten könnte, nachdem sie eine solche Diagnose ausgegeben hat, ob es eine Erweiterung dokumentiert oder nicht, kann die Anforderung, Erweiterungen zu dokumentieren, nur sinnvoll auf Erweiterungen angewendet werden, die keine Diagnosen generieren. Das wiederum würde darauf hindeuten, dass solche Dinge zulässig sein müssen.

Der Standard verlangt, dass Erweiterungen das Verhalten von streng konformen Programmen nicht nachteilig beeinflussen, aber da kein solches Programm eine VLA-Deklaration innerhalb einer Struktur enthalten kann, ist diese Anforderung hier kein Problem.

  • Es ist immer noch kein dokumentiertes “Feature” der GCC-Sprache (eigentlich des ADA-Einflusses auf das GCC-IR-Design), und es gibt keine Fehler, bis die Option nicht allgemein bekannt ist -pedantic-errors hinzugefügt. Es ist kein Merkmal der Sprache, es ist ein Merkmal des Compilers, und das Hinzufügen dieses Merkmals auf undokumentierte Weise und ohne ausreichende Warnung führt zu einer breiten Verwendung eines solchen Merkmals (z. B. im Linux-Kernel) und es ist irgendwie Vendor-Lock-in da es verhindert, dass der Kernel mit clang/icc/einem anderen nicht-GCC-basierten Compiler kompiliert wird, was zu einem GCC-Monopol führt

    – osgx

    6. März 2017 um 19:16 Uhr


  • Keine Warnungen mit -std=c99 -Wall -Wextra mit gcc; nur Warnung mit -std=c99 -pedantic: “vlais.c:5:10: warning: a member of a structure or union cannot have a variably modified type [-Wpedantic]\n int params[argc]; // << Wat?“. Die Autoren des Kernels haben diese undokumentierte Erweiterung an mehreren Stellen im Linux-Kernel verwendet, und das Ändern des Codes in standardkompatiblen Code kostet Zeit und viel zusätzlichen Aufwand: linuxplumbersconf.org/2013/ocw/sessions/1221 (Sie sagen auch über Clang “es unterstützt kein VLAIS, weil es vom C-Standard ausdrücklich verboten ist”). C99 erlaubt VLA am Ende von st

    – osgx

    6. März 2017 um 19:22 Uhr


  • @osgx: Die Autoren des Standards haben sich keine Mühe gegeben, zu fordern, dass alle Implementierungen alle erforderlichen Funktionen enthalten, um sie für alle Zwecke (oder sogar für alle Zwecke) geeignet zu machen irgendein besonderer Zweck). Die meisten nützlichen Zwecke, die mit C erfüllt werden können, erfordern Funktionen, die nicht in allen Implementierungen vorhanden sind, und der Weg, eine Anbieterbindung zu vermeiden, besteht darin, dass Implementierungen nützliche Funktionen unterstützen, die von anderen unterstützt werden. Der Standard sollte niemals als Entschuldigung dafür dienen, nützliche Funktionen oder Garantien nicht zu unterstützen.

    – Superkatze

    6. März 2017 um 19:26 Uhr


  • Überprüfen Sie auch thelinuxjedi.blogspot.ru/2014/02/why-vlais-is-bad.html über den Hintergrund des “Features” und dessen Wirkung. Ebenfalls events.linuxfoundation.org/sites/events/files/slides/…. Es dauerte mehrere Jahre (2012-2016), um die meisten VLAIS aus dem Kernel zu entfernen, was es schwieriger machte, mit clang kompiliert zu werden (und mehrere Sicherheitsfehler mit clang/sanitizers/fuzzers wie syzkaller zu finden).

    – osgx

    6. März 2017 um 19:31 Uhr


  • @osgx: Ich sagte “sollte es versuchen”. Das ist kein besonders starker Imperativ. Mein Punkt ist, dass, wenn ein Feature nützlich wäre und ein Compiler es leicht unterstützen könnte, er dies nicht unterlassen sollte nur weil der Standard eine solche Unterstützung nicht fördert. Andererseits denke ich, dass die Autoren sowohl von gcc als auch von clang dazu neigen, den Standard als einschränkend zu betrachten, was Programmierer von Implementierungen erwarten sollten, obwohl C89 nie so gemeint war und viele der späteren Standards Teile von C89 beibehalten haben was so nicht sinnvoll interpretiert werden kann.

    – Superkatze

    6. März 2017 um 20:12 Uhr

1333250cookie-checkArray variabler Länge in der Mitte der Struktur – warum dieser C-Code für gcc gültig ist

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

Privacy policy