Generieren Sie eine Compiler-Warnung, wenn das Initialisierungskomma für const char * -Array fehlt

53

Ich verwende häufig String-Literal-Tabellen in meinem C-Code. Diese Tabellen sehen alle mehr oder weniger so aus:

static const char* const stateNames[STATE_AMOUNT] =
{
    "Init state",
    "Run state",
    "Pause state",
    "Error state",
};

Das Problem mit dem obigen Code ist, wenn die Tabelle länger wird und während der Entwicklung geändert wird, vergesse ich von Zeit zu Zeit ein Komma. Der Code wird problemlos mit einem fehlenden Komma kompiliert, aber mein Programm stürzt ab, wenn die letzte Zeichenfolge auf gesetzt ist NULL. Ich habe die MinGW- und Keil-Compiler zur Überprüfung verwendet.

Gibt es eine Möglichkeit, eine Compiler-Warnung für meine Initialisierung zu generieren, wenn das Komma fehlt?

Jonny Schubert
quelle
1
Was passiert, wenn Sie einfach vergessen, dieser Tabelle einen Status hinzuzufügen?
Jeroen3
1
@ Jeroen3 true dies würde den gleichen Fehler verursachen. Die Verwendung einer statischen Zusicherung, die die Listenlänge gegen STATE_AMOUNT testet, löst auch dieses Problem.
Jonny Schubert

Antworten:

62

Wenn Sie alle const char*in Klammern setzen, sollte das Problem wie im folgenden Ausschnitt gezeigt gelöst werden:

static const char* const stateNames[5] =
{
    ("Init state"),
    ("Run state"),
    ("Pause state")     //comma missing
    ("Pause state3"),
    ("Error state")
};

Wenn Sie ein Komma vergessen, wird ein Kompilierungsfehler angezeigt, der ähnlich ist wie: error: called object is not a function or function pointer

LIVE DEMO


Beachten Sie, dass wenn Sie das Komma vergessen, C tatsächlich die zwei (oder mehr) Zeichenfolgen bis zum nächsten Komma oder dem Ende des Arrays verkettet. Angenommen, Sie vergessen das Komma wie folgt:

static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state" //comma missing
    "Pause state3" //comma missing
    "Error state"
};

int main(void)
{  
    printf("%s\n", stateNames[0]);
    return 0;    
}

Dies wird gcc-9.2generiert (andere Compiler generieren ähnlichen Code):

.LC0:
        .string "Init state"
        .string "Run state"
        .string "Pause statePause state3Error state" ; oooops look what happened
        .quad   .LC0
        .quad   .LC1
        .quad   .LC2
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, OFFSET FLAT:.LC0
        mov     rdi, rax
        call    puts
        mov     eax, 0
        pop     rbp
        ret

Es ist klar, dass die letzten drei Zeichenfolgen verkettet sind und das Array nicht die erwartete Länge hat.

Davide Spataro
quelle
33

Sie können den Compiler das Array zählen lassen und bei unerwartetem Ergebnis eine Fehlermeldung generieren:

enum { STATE_AMOUNT = 4 };

static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state"    // <--- missing comma
    "Error state",
};

_Static_assert( sizeof stateNames / sizeof *stateNames == STATE_AMOUNT,
        "oops, missed a comma" );

In diesem Thread finden Sie Ideen zur Implementierung, _Static_assertwenn Ihr Compiler sehr alt ist und ihn nicht unterstützt.

Als Bonus kann dies auch hilfreich sein, wenn Sie neue Status hinzufügen, aber vergessen, die Zeichenfolgentabelle zu aktualisieren. Vielleicht möchten Sie sich aber auch mit X-Makros befassen.

MM
quelle
Verdammt ... das war genau die Antwort, die ich gerade tippen wollte!
Der Schweißer
11

Ich habe immer einen Verweis auf ein Array mit expliziter Größe verwendet, um dies zu lösen.

// no explicit size here
static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state",
    "Error state",
};
static const char* const (&stateNameVerifier)[STATE_AMOUNT] = stateNames;

http://coliru.stacked-crooked.com/a/593fc2eac80782a6

main.cpp:10:32: error: reference to type 'const char *const [5]' could not bind to an lvalue of type 'const char *const [4]'
static const char* const (&stateNameVerifier)[STATE_AMOUNT] = stateNames;
Mooing Duck
quelle
4
Eine statische Behauptung scheint eine viel elegantere Lösung zu sein. Ich nehme an, Sie haben sich angewöhnt, dies zu tun, bevor statische Aussagen als Teil der Sprache implementiert wurden? Sehen Sie jetzt noch einen Vorteil gegenüber einer statischen Zusicherung, die die erwartete Größe des Arrays überprüft?
Cody Gray
2
@CodyGray: Ja, das war eine vorstatische Behauptung, jetzt wo du es
sagst
9

Dies bringt den Compiler nicht dazu, Ihnen zu helfen, aber ich finde, dass das Schreiben wie unten es für Menschen einfacher macht, kein Komma zu setzen:

static const char* const stateNames[STATE_AMOUNT] =
{
      "Init state"
    , "Run state"
    , "Pause state"
    , "Error state"
};
JonathanZ unterstützt MonicaC
quelle
3
Auch das Anhängen von etwas am Ende ist einfacher. Sie müssen die vorherige Zeile nicht bearbeiten, um ein Komma hinzuzufügen. (Der Hauptgrund für das fehlende Komma.)
Datenfiddler
@ Datafiddler: Einverstanden. Dies ist auch nützlich, um die Liste der Spalten in einem SQL SELECT-Befehl zu optimieren, wenn Sie sie kommentieren und auskommentieren. Sie möchten oft den letzten ändern. Sie möchten selten die erste ändern. Auf diese Weise müssen Sie nicht mehrere Zeilen ändern, um ein Element auskommentieren zu können.
JonathanZ unterstützt MonicaC