Was ist die Notwendigkeit von leeren Klammern '{}' am Ende eines Arrays von Strukturen?

59

Ich habe einen Code im Linux-Kernel getroffen:

static struct ctl_table ip_ct_sysctl_table[] = {
    {
        .procname   = "ip_conntrack_max",
        .maxlen     = sizeof(int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec,
    },
    // ...
    {
        .procname   = "ip_conntrack_log_invalid",
        .maxlen     = sizeof(unsigned int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec_minmax,
        .extra1     = &log_invalid_proto_min,
        .extra2     = &log_invalid_proto_max,
    },
    { }
};

Hier endet ein Array von Strukturen mit { }. Zu welchem ​​Zweck wurde es hinzugefügt?
Etwas oberhalb dieses Codes befindet sich übrigens ein weiteres Array von Strukturen , jedoch ohne leere Klammern am Ende.

Wann sollte ich am Ende eines Arrays von Strukturen leere Klammern verwenden?

NK-Zelle
quelle
1
Hmm was ist, wenn es hinzugefügt wird, um das Ende des Arrays zu signalisieren, wie 0 das Ende des Strings signalisiert? Einfach raten.
Eraklon
4
Dies ist eine nicht standardmäßige GCC-Erweiterung. Und als solches kommt es sehr wahrscheinlich mit wenig oder keiner Dokumentation ... Ich habe gerade alle Dokumente gelesen und kann nichts über leere Strukturinitialisierungslisten finden. Es wird jedoch kompiliert, es sei denn, Sie erzwingen strenge ISO mit -pedantic.
Lundin
9
Wie auch immer, es ist ein "Sentinel" -Wert, ein Element, bei dem alles auf Null / NULL gesetzt ist, um das Ende des Arrays zu markieren.
Lundin
Sentinels sind auch in CPython-Erweiterungsmodulen üblich .
MaxPowers

Antworten:

38

Diese spezielle Änderung war Teil des sysctl-Netzes: Entfernen Sie nicht verwendeten binären sysctl-Code , der von Eric W. Biederman festgeschrieben wurde, und ändern Sie die Initialisierung des letzten Elements des ip_ct_sysctl_tableArrays von {0}auf {}(und führen Sie ähnliche Änderungen wie bei vielen anderen Array-Initialisierungen durch).

Das {0}Muster scheint jedoch schon viel länger zu existieren, und beide {0}oder die {}endgültige Elementinitialisierung wird üblicherweise (im Linux-Quellcode) explizit als bezeichnet Terminating entry, sodass es wahrscheinlich ein Muster ist, das das Konsumieren dieser Arrays ohne Kenntnis ihrer Länge ermöglicht. Verbrauch beenden, wenn der mit Null initialisierte Abschlusseintrag erreicht wird. ZB für die ähnlichen Arrays in sound/aoa/fabrics/snd-aoa-fabric-layout.cder Absicht der Nullinitialisierung wird sogar explizit in einem Kommentar erwähnt, zB:

static struct codec_connection toonie_connections[] = {
  {
      .connected = CC_SPEAKERS | CC_HEADPHONE,
      .codec_bit = 0,
  },
  {} /* terminate array by .connected == 0 */
};
dfri
quelle
11
Es wäre interessant zu wissen, warum sie Standard C zugunsten einer GCC-Erweiterung fallen lassen, die hinsichtlich der Funktionalität zu 100% gleichwertig ist. Es wird lediglich verhindert, dass der Code auf Standard-C-Compilern kompiliert wird. Das heißt, angeblich 100% äquivalent, da gcc diese Funktion nicht zu dokumentieren scheint ... Dies ist kein Array mit der Länge Null, sondern eine leere Initialisierungsliste.
Lundin
@Lundin Würde nicht int arr[] = {}( vorausgesetzt , wir verwenden die leere Initialisierer-Erweiterung GNU) zu einem leeren Array führen; dh die Größe des arrSeins 0?
2.
1
@Lundin: Die cppreference-Seite steht jedoch im Widerspruch zum Wortlaut von ISO / IEC 9899: 2011, der dies zulässt (§6.7.9 (21)). Kein Initialisierer ist zweifellos "weniger" als Mitglieder des Aggregats. Das ist also keine queere Compiler-Erweiterung, sondern eine legitime C.
Damon
2
@Damon Es ist nicht gültig C und es ist bekannt ... kompilieren mit gcc -pedantic-Fehlern. Um zu verstehen, warum, müssen Sie die tatsächliche Syntax für eine Initialisierungsliste oben in 6.7.9 lesen. Es muss mindestens ein Initialisierer vorhanden sein. Hier erklärt: stackoverflow.com/questions/17589533/… . Speziell { initializer-list }dann Initialisierungsliste: designation(opt) initializeroderinitializer-list , designation(opt) initializer
Lundin
2
@Lundin In diesem speziellen Fall keine Ahnung. Gcc-Erweiterungen werden jedoch häufig im Linux-Kernel verwendet.
Bobburner
20

Sie sind wahrscheinlich mit nullterminierten Zeichenfolgen vertraut. ctl_table ip_ct_sysctl_table[]ist ein nullterminiertes Array, dh der letzte Arrayeintrag hat alle null Mitglieder.

MSalters
quelle
1
Wenn Sie also das Array durchgehen, wissen Sie, dass Sie das Ende erreicht haben, wenn z. B. procnamenull oder maxlennull ist.
Paul Ogilvie
1
@PaulOgilvie: Nun, das Beispiel ist unvollständig. procnamekönnte ein sein, char[100]in welchem ​​Fall es ist "", nicht null. Aber sonst ja.
MSalters
13

Was ist die Notwendigkeit von leeren Klammern '{}' am Ende eines Arrays von Strukturen?

Um es klar auszudrücken: Die "leeren Klammern '{}' am Ende eines Arrays von Strukturen" werden nicht benötigt , um die C-Syntaxanforderungen zu erfüllen.

Wann sollte ich am Ende eines Arrays von Strukturen leere Klammern verwenden?

Wenn Code einen Sentinel-Wert wünscht .

Manchmal ist es für das Programm nützlich , ein endgültiges Array-Element aller Nullen zu haben - sicherlich, um das Ende zu erkennen. Der Bedarf ergibt sich aus der Verwendung eines Arrays durch die Anwendung ctl_table ip_ct_sysctl_table[], nicht aus einem C-Sprachbedarf.

chux - Monica wieder einsetzen
quelle
9

Es ist ein mit Null initialisiertes Element am Ende des Arrays, um die Anzahl der Elemente des Arrays um eins zu erhöhen.

Betrachten Sie diese kleine Demo:

#include <stdio.h>

struct Test
{
  int x;
  int y;
} arr[] =
{
    {1,2},
    {3,4},
//  {}
};

int main(void) {
    printf("%zu\n", sizeof(arr) / sizeof(arr[0]));
    return 0;
}

Die Größe des arrArrays ändert sich, wenn Sie das {}am Ende der Array-Initialisierungsliste auskommentieren.

Ausgänge:

Mit // {}(Array hat 2 Elemente)

2

Mit {}(Array hat 3 Elemente)

3

Weitere Erklärung:

Das ip_ct_sysctl_tableArray wird nur an einer Stelle verwendet, also hier:

in->ctl_table = kmemdup(ip_ct_sysctl_table,
                sizeof(ip_ct_sysctl_table),
                GFP_KERNEL);

Das Extra {}erhöht die Gesamtgröße ip_ct_sysctl_table.

Jabberwocky
quelle
1
Das ist nicht "um die Anzahl der Elemente des Arrays zu erhöhen", sondern um das Ende des Arrays zu signalisieren.
Paul Ogilvie
6
Lol Nein. Die Idee ist, dass bisher niemand in der Lage war, dies mit absoluter Sicherheit vollständig zu erklären. Die nächste Gewissheit ist einfach, dass { }es sich um einen Initialisierer handelt. Aber das Warum ist noch unklar. So kann jetzt sowieso, das Wort wahrscheinlich ist wahrscheinlich eine gute Idee. :)
Ryyker