Bestimmen Sie #definierte Zeichenfolgenlänge zur Kompilierzeit

Lesezeit: 6 Minuten

Benutzer-Avatar
Alexander Färber

Ich habe ein C-Programm (ein Apache-Modul, dh das Programm läuft oft), was geht write() eine 0-terminierte Zeichenfolge über einen Socket, daher muss ich ihre Länge kennen.

Die Zeichenfolge ist #definiert als:

#define POLICY "<?xml version=\"1.0\"?>\n" \
   "<!DOCTYPE cross-domain-policy SYSTEM\n" \
   "\"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">\n" \
   "<cross-domain-policy>\n" \
   "<allow-access-from domain=\"*\" to-ports=\"8080\"/>\n" \
   "</cross-domain-policy>\0"

Gibt es bitte einen Weg, besser als die Verwendung strlen(POLICY)+1 zur Laufzeit (und damit die Länge immer wieder neu berechnen)?

Eine Präprozessordirektive, die eine Einstellung ermöglichen würde POLICY_LENGTH schon zur kompilierungszeit?

Benutzer-Avatar
Oliver Charlesworth

Verwenden sizeof(). z.B sizeof("blah") wird zu bewerten 5 zur Kompilierzeit (5, nicht 4, da das Zeichenfolgenliteral immer ein implizites Null-Terminierungszeichen enthält).

  • Vielleicht möchten Sie sicherstellen, dass klar ist, warum es mit 5 und nicht mit 4 bewertet wird.

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

    23. Oktober 2010 um 19:51 Uhr

  • sizeof(“blah”) ist 5. Aber das Problem ist, dass sizeof(blah) wo char * blah = "blah" ist 4, da es sich um eine Zeigergröße in einem 32-Bit-System handelt. Das ist ein Problem, weil wir sizeof(“concrete string”) nicht brauchen. Wir brauchen sizeof(given_pointer). Sie müssen stattdessen strlen(pointer) verwenden, aber es ist sux und die Notwendigkeit, +1 hinzuzufügen, ist hier ein kleines Problem.

    – Val

    1. April 2013 um 8:53 Uhr

  • @Val in der Tat sizeof() gibt die falsche Antwort, wenn ein Zeiger anstelle eines Arrays übergeben wird; und wenn Sie sich angewöhnen, es zur Bestimmung der Länge von String-Literalen zu verwenden, ist es leicht, versehentlich a zu übergeben char* Array statt. Allerdings mit C11 _Generic Wir können ein typsicheres Makro erstellen, das fehlschlägt, wenn ein Zeiger übergeben wird, siehe meine Antwort: stackoverflow.com/a/72836532

    – Simon Kissane

    2. Juli um 4:53

Verwenden 1+strlen(POLICY) und aktivieren Sie Compiler-Optimierungen. GCC ersetzt strlen(S) zur Kompilierzeit durch die Länge von S, wenn der Wert von S zur Kompilierzeit bekannt ist.

sizeof funktioniert zur Kompilierzeit

#define POLICY "<?xml version=\"1.0\"?>\n" \
   "<!DOCTYPE cross-domain-policy SYSTEM\n" \
   "\"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">\n" \
   "<cross-domain-policy>\n" \
   "<allow-access-from domain=\"*\" to-ports=\"8080\"/>\n" \
   "</cross-domain-policy>\0"

char pol[sizeof POLICY];
strcpy(pol, POLICY); /* safe, with an extra char to boot */

Wenn Sie ein Präprozessorsymbol mit der Größe benötigen, zählen Sie einfach die Zeichen und schreiben Sie das Symbol selbst 🙂

#define POLICY_LENGTH 78 /* just made that number up! */

  • @Oli: Ich vermute, pmg meinte einen Ausdruck, der für die Verwendung in Präprozessorbedingungen gültig ist, was sizeof ist nicht..

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

    23. Oktober 2010 um 19:50 Uhr

Ich habe ein ähnliches Problem, wenn ich einen veralteten Compiler (VisualDSP) auf einer eingebetteten Plattform verwende, die C++ 11 noch nicht unterstützt (und daher kann ich constexpr nicht verwenden).

Ich muss die Zeichenfolgenlänge im Precompiler nicht auswerten, aber ich muss sie in eine einzelne Zuweisung optimieren.

Nur für den Fall, dass jemand dies in Zukunft braucht, hier ist meine extrem hackige Lösung, die selbst auf beschissenen Compilern funktionieren sollte, solange sie die richtige Optimierung durchführen:

#define STRLENS(a,i)        !a[i] ? i : // repetitive stuff
#define STRLENPADDED(a)     (STRLENS(a,0) STRLENS(a,1) STRLENS(a,2) STRLENS(a,3) STRLENS(a,4) STRLENS(a,5) STRLENS(a,6) STRLENS(a,7) STRLENS(a,8) STRLENS(a,9) -1)
#define STRLEN(a)           STRLENPADDED((a "\0\0\0\0\0\0\0\0\0")) // padding required to prevent 'index out of range' issues.

Dieses STRLEN-Makro gibt Ihnen die Länge des von Ihnen bereitgestellten Zeichenfolgenliterals an, solange es weniger als 10 Zeichen lang ist. In meinem Fall reicht das aus, aber im OP-Fall muss das Makro möglicherweise (viel) erweitert werden. Da es sehr repetitiv ist, könnten Sie leicht ein Skript schreiben, um ein Makro zu erstellen, das 1000 Zeichen akzeptiert.

PS: Dies ist nur ein einfacher Ableger des Problems, das ich wirklich zu beheben versuchte, nämlich ein statisch berechneter HASH-Wert für eine Zeichenfolge, sodass ich keine Zeichenfolgen in meinem eingebetteten System verwenden muss. Falls jemand interessiert ist (es hätte mir einen Tag des Suchens und Lösens erspart), wird dies einen FNV-Hash für ein kleines Zeichenfolgenliteral erstellen, das zu einer einzigen Zuweisung optimiert werden kann:

#ifdef _MSC_BUILD
#define HASH_FNV_OFFSET_BASIS   0x811c9dc5ULL
#define HASH_TYPE               int
#else   // properly define for your own compiler to get rid of overflow warnings
#define HASH_FNV_OFFSET_BASIS   0x811c9dc5UL
#define HASH_TYPE               int
#endif
#define HASH_FNV_PRIME          16777619
#define HASH0(a)                (a[0] ? ((HASH_TYPE)(HASH_FNV_OFFSET_BASIS * HASH_FNV_PRIME)^(HASH_TYPE)a[0]) : HASH_FNV_OFFSET_BASIS)
#define HASH2(a,i,b)            ((b * (a[i] ? HASH_FNV_PRIME : 1))^(HASH_TYPE)(a[i] ? a[i] : 0))
#define HASHPADDED(a)           HASH2(a,9,HASH2(a,8,HASH2(a,7,HASH2(a,6,HASH2(a,5,HASH2(a,4,HASH2(a,3,HASH2(a,2,HASH2(a,1,HASH0(a))))))))))
#define HASH(a)                 HASHPADDED((a "\0\0\0\0\0\0\0\0\0"))

Für andere Antworten, sizeof(STRING) gibt die Länge an (inkl \0 Abschlusszeichen) für Zeichenfolgenliterale. Es hat jedoch einen Nachteil: Wenn Sie es versehentlich passieren, a char* Zeigerausdruck anstelle eines Zeichenfolgenliterals, wird ein falscher Wert zurückgegeben – die Zeigergröße, die 4 für 32-Bit-Programme und 8 für 64-Bit-Programme ist – wie das folgende Programm demonstriert:

#include <stdio.h>

#define foo     "foo: we will give the right answer for this string"
char    bar[] = "bar: and give the right answer for this string too";
char   *baz   = "baz: but for this string our answer is quite wrong"; 

#define PRINT_LENGTH(s) printf("LENGTH(%s)=%zu\n", (s), sizeof(s))

int main(int argc, char **argv) {
   PRINT_LENGTH(foo);
   PRINT_LENGTH(bar);
   PRINT_LENGTH(baz);
   return 0;
}

Wenn Sie jedoch C11 oder höher verwenden, können Sie verwenden _Generic um ein Makro zu schreiben, das sich weigert zu kompilieren, wenn etwas anderes als a übergeben wird char[] Array:

#include <stdio.h>

#define SIZEOF_CHAR_ARRAY(s) (_Generic(&(s), char(*)[sizeof(s)]: sizeof(s)))

#define foo     "foo: we will give the right answer for this string"
char    bar[] = "bar: and give the right answer for this string too";
char   *baz   = "baz: but for this string our answer is quite wrong"; 

#define PRINT_LENGTH(s) printf("LENGTH(%s)=%zu\n", (s), SIZEOF_CHAR_ARRAY(s))

int main(int argc, char **argv) {
    PRINT_LENGTH(foo);
    PRINT_LENGTH(bar);
    // Will fail to compile if you uncomment incorrect next line:
    // PRINT_LENGTH(baz);
    return 0;
}

Beachten Sie, dass dies nicht nur für String-Literale funktioniert – es funktioniert auch korrekt für veränderliche Arrays, vorausgesetzt, sie sind aktuell char[] Arrays fester Länge als String-Literal ist.

Wie oben geschrieben SIZEOF_CHAR_ARRAY Makro wird für fehlschlagen const Ausdrücke (obwohl Sie denken würden, dass String-Literale das sein sollten constaus Gründen der Abwärtskompatibilität sind sie es nicht):

const char quux[] = "quux is const";
// Next line will fail to compile:
PRINT_LENGTH(quux);

Wir können uns jedoch verbessern SIZEOF_CHAR_ARRAY Makro, sodass das obige Beispiel auch funktioniert:

#define SIZEOF_CHAR_ARRAY(s) (_Generic(&(s), \
    char(*)[sizeof(s)]: sizeof(s), \
    const char(*)[sizeof(s)]: sizeof(s) \
))

1365080cookie-checkBestimmen Sie #definierte Zeichenfolgenlänge zur Kompilierzeit

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

Privacy policy