C/C++ warnt oder verbietet die Verkettung literaler Zeichenfolgen

Lesezeit: 6 Minuten

gozags Benutzeravatar
gozag

Gibt es eine Möglichkeit, wörtliche Zeichenfolgenverkettungen zu warnen oder zu verbieten, wie zum Beispiel:

const char *a = "foo" " bar";

Ich habe Stunden damit verbracht, einen Fehler in einem großen statischen Array zu finden

const char * a[] = {"foo" "bar"};

anstatt

const char * a[] = {"foo", "bar"};

  • Ich habe Mitleid mit deinem Schmerz. Wir waren wahrscheinlich alle dort. Sie möchten die Verkettung von Zeichenfolgen jedoch nicht vollständig verbieten, da ein Großteil des Codes absichtlich davon abhängt.

    – Steve Summit

    19. Mai um 11:44 Uhr


  • Ich verwende dies, wenn ich sehr viele Protokollzeichenfolgen habe, daher wäre eine Warnung dieser Art für mich ärgerlich. Da diese Funktion tatsächlich genutzt wird, glaube ich nicht, dass es eine einfache Möglichkeit gibt, die Erkennung dieses Tippfehlers zu automatisieren. Sie sollten sicherstellen, dass der Code gut getestet ist.

    – Marek R

    19. Mai um 11:47 Uhr


  • @MarekR Ja, es ist nicht narrensicher. aber das muss nicht sein. Wichtiger ist es, nichts zu verpassen. In der Praxis ist Ihr Beispiel wahrscheinlich sehr selten. Eine weitere zu überprüfende Sache sind Zeilen, die mit enden ", möglicherweise wird ein Leerzeichen nachgestellt. Die können damit gefangen werden grep '"\s*$' wenn gewünscht.

    – Tom Karzes

    19. Mai um 12:17 Uhr

  • Die Verkettung literaler Zeichenfolgen ist logisch Phase 6 im Kompilierungsprozess, der vor der Tokenisierung stattfindet. Vermutlich können Sie nichts gegen das Problem tun.

    – Richard Critten

    19. Mai um 12:17 Uhr


  • Einige Kandidaten (und/oder Ausgangspunkte): Gibt es ein GCC-Flag zum Erkennen der Verkettung von Zeichenfolgenliteralen? (2015. „Ich habe kürzlich einen Fehler behoben … jemand hat einen vergessen , nach string3) Und Warum die Verkettung von String-Literalen zulassen? (2010. „Ich wurde kürzlich von einem subtilen Käfer gebissen … das habe ich vergessen , nach two)

    – Peter Mortensen

    21. Mai um 11:29 Uhr

cigiens Benutzeravatar
cigien

Clang hat eine Warnung -Wstring-Verkettung das explizit darauf ausgelegt ist, solche Fehler abzufangen:

warning: suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma? [-Wstring-concatenation]
char const *a[]  = { "ok", "foo" "bar", "ok"};
                                 ^
                                ,

Dies wird für das Spielzeugbeispiel, das Sie gezeigt haben, nicht genau funktionieren, da Sie mehrere Initialisierer benötigen und nur an einigen Stellen Kommas fehlen, z. B.:

// no warning
char const *b[]  = {"foo" "bar"};
// no warning
char const *c[]  = {"ok", "foo" "bar"};
// no warning
char const *d[]  = {"foo" "bar", "ok"};

Wenn Sie jedoch eine große Anzahl von Initialisierern in einem Array haben und nur an einigen Stellen einen Tippfehler machen, scheint dies ideal zu sein.

Hier ist ein Demo.

GCC scheint keine entsprechende Warnung zu haben, aber es gibt eine Anfrage damit es hinzugefügt wird.

Beachten Sie, dass dies nur für die Array-Initialisierung funktioniert. Ihr Beispiel für

const char *x = "foo" " bar";

wird von dieser Warnung (oder einer anderen mir bekannten Warnung) nicht erkannt.

Beachten Sie auch, dass die Aktivierung dieser Warnung zu vielen Fehlalarmen führen kann. Sie können sie jedoch sparsam einsetzen, wenn Sie versuchen, Fehler zu erkennen.

  • Habe auch eines für clang-tidy gefunden! clang.llvm.org/extra/clang-tidy/checks/bugprone/…

    – gozag

    19. Mai um 12:35 Uhr

  • Ich frage mich, warum es hier nicht funktioniert: godbolt.org/z/W4cevbenj

    – Marek R

    19. Mai um 12:58

  • GCC warnt im Allgemeinen vor der Verkettung von Zeichenfolgen mit -Wtraditional (nur C), aber die Aktivierung dieser Option ist wahrscheinlich nicht zu empfehlen.

    – Nielsen

    19. Mai um 14:32 Uhr

  • Gibt es ein GCC-Flag zum Erkennen der Verkettung von Zeichenfolgenliteralen?

    – phuclv

    21. Mai um 10:50 Uhr

Nicht wirklich. Die Verkettung von String-Literalen ist ein unverzichtbarer Bestandteil der C/C++-Grammatik und hat viele Anwendungsfälle. Es ist also eine gewisse Anstrengung erforderlich, die das Ziel, einen Fehler zu erkennen, möglicherweise zunichte macht.

Allerdings funktioniert die String-Verkettung sehr streng bei zwei String-Literalen, die nacheinander mit nur Leerzeichen dazwischen erscheinen, sodass das Unterbrechen des Leerraums zu einem Fehler führt. In diesem Fall hätten Sie beispielsweise schreiben können:

const *char[] = {("foo") ("bar")};  // Error

was einen Fehler verursachen würde, während die beabsichtigte Anweisung Folgendes nicht tun würde:

const *char[] = {("foo"), ("bar")};  // OK

Kurz gesagt, Sie können dem Compiler nicht explizit mitteilen, dass zwei Zeichenfolgenliterale möglicherweise verkettet sind und in allen anderen Fällen fehlschlagen. Daher müssen Sie dem Compiler explizit mitteilen, wann ein Zeichenfolgenliteral möglicherweise verkettet ist nicht verkettet werden.

  • „…dem Compiler explizit mitteilen, wann ein String-Literal nicht verkettet werden darf …“ Wie wäre es mit einem , zwischen ihnen? Ich habe das Gefühl, dass wir den Kreis geschlossen haben.

    – Richard Critten

    19. Mai um 12:19 Uhr


  • @RichardCritten Ja, der Hauptpunkt ist, dass ich denke, dass die Lösung, nach der das OP sucht, in den C/C++-Compilerdiensten nicht existiert.

    – Nielsen

    19. Mai um 12:24


  • Ich denke, die meisten „unverzichtbaren“ Verwendungszwecke beinhalten die Verkettung von String-Literalen mit Makros, z. B. den Makros, die in Strings im Printf-Format verwendet werden. Es kommt seltener vor, dass Sie wirklich nur String-Literale verketten müssen.

    – Barmar

    20. Mai um 23:04

  • Ich verkette ständig reine String-Literale, ohne dass Makros beteiligt sind, einfach weil ich gerne Codezeilen umbreche. Die vorgeschlagene Lösung, jede einzelne Zeichenfolge/jedes einzelne Element in Klammern zu setzen, ist faszinierend. Trotz ziemlich umfangreicher Erfahrung im Schreiben und Lesen von C- und C++-Code hätte ich nicht sofort gewusst, ob diese Syntax gültig ist. Es macht auf jeden Fall Sinn, aber es kommt mir auch faul vor. Ich würde dies gerne in einer Codeüberprüfung melden. Es ist zwar interessant und stellt eine mögliche Lösung für das erwähnte Problem dar, schließt aber, wie Richard vorschlägt, den Kreis.

    – Cody Gray – im Streik

    21. Mai um 7:10 Uhr


  • @CodyGray: Ich fand die Parens anfangs überraschend, aber es dauerte nicht lange, bis ich darüber nachdachte, warum sie gültig sind: Ein String-Literal ist ein Objekt vom Typ const char* (oder evtl char* in C habe ich vergessen). Klammern können in Ausdrücken vorkommen und als umschlossener Unterausdruck ausgewertet werden. Eine Initialisierungsliste möchte eine Liste von const char* Ausdrücke. Ich musste ein paar Sekunden darüber nachdenken, aber wenn eine Codebasis dies überall verwenden würde, wäre ich daran gewöhnt. Die größere Frage ist also, ob dieses zusätzliche syntaktische Rauschen beim Lesen des Codes schlimmer ist als das mögliche Problem.

    – Peter Cordes

    22. Mai um 16:33 Uhr

Juans Benutzeravatar
Juan

Mit jedem der folgenden Makros ist es unmöglich, versehentlich zwei Zeichenfolgen zu verketten.

CPP (C-Präprozessor) Makros sind im Allgemeinen großartig. Es ist auch zulässig, am Ende einer Elementliste ein abschließendes Komma zu verwenden.

Sie können so etwas tun:

#define STRINGCOMMA(a) a,

const char *x[] = {
    STRINGCOMMA("foo")
    STRINGCOMMA("bar")
};

Oder auch:

#define QUOTESTRINGCOMMA(a) #a,

const char *x[] = {
    QUOTESTRINGCOMMA(foo)
    QUOTESTRINGCOMMA(bar)};

Das Komma wird für Sie hinzugefügt und es wäre illegal, wenn Sie es versehentlich selbst tun würden.

Wenn Sie interessiert sind, ist es auch möglich, dieses Konzept weiterzuentwickeln, um die Erstellung paralleler Listen mit denselben Argumenten, aber unterschiedlicher Verarbeitung zu ermöglichen:

X-Makro

#define VARLIST \
  DO(foo) \
  DO(bar)

#define DO(a) #a,
  const char *x[] = {
VARLIST
};
#undef DO

Dies wäre nützlich, wenn Sie aus derselben Namensliste eine Liste mit Aufzählungen und eine Liste mit Zeichenfolgen erstellen möchten.

Ich habe Stunden damit verbracht, einen Fehler in einem großen statischen Array zu finden …

Nun, Sie können Folgendes tun:

char const * a [] = 
    { "foo"
    , "bar"
    , "baz"
    , "asdf"
    , "ghjk"
    };

1454050cookie-checkC/C++ warnt oder verbietet die Verkettung literaler Zeichenfolgen

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

Privacy policy