2012-11-21 12 views
12
int main() 
{ 
    char a[7] = "Network"; 
    return 0; 
} 

A Stringliteral in C ist intern mit einem NUL-Zeichen beendet. Daher sollte der obige Code einen Kompilierungsfehler ergeben, da die tatsächliche Länge des Zeichenfolgeliterals Network 8 ist und nicht in ein char[7] Array passen kann.Warum erlaubt gcc char-Array-Initialisierung mit String-Literal größer als Array?

Aber gcc (auch mit -Wall) auf Ubuntu kompiliert diesen Code ohne Fehler oder Warnung. Warum lässt gcc dies zu und meldet es nicht als Kompilierungsfehler?

gcc gibt nur eine Warnung aus (immer noch kein Fehler!), Wenn die Char-Array-Größe kleiner als das String-Literal ist. Zum Beispiel, es warnt auf:

char a[6] = "Network"; 

[Verwandte] Visual C++ 2012 gibt einen Kompilierungsfehler für char a[7]:

1>d:\main.cpp(3): error C2117: 'a' : array bounds overflow 
1> d:\main.cpp(3) : see declaration of 'a' 
+6

Vielleicht kompilieren Sie den Code als C mit gcc und als C++ in Visual Studio. Sie sind nur scheinbar ähnliche Sprachen. – nos

+0

@nos: Ja, das erklärt das Verhalten! Ich denke, das ist einer dieser kleinen Unterschiede im C-ähnlichen Code zwischen C und C++. –

+1

Es wird ein Fehler mit g ++ sein, wenn ich mich richtig erinnere – fayyazkl

Antwort

21

Initialisieren einen char-Array mit einer String-Literal, die größer ist, als es ist schön in C, aber falsch in C++. Das erklärt den Unterschied im Verhalten zwischen gcc und VC++.

Sie erhalten keinen Fehler, wenn Sie das gleiche wie eine C-Datei mit VC++ kompiliert haben. Und Sie würden einen Fehler erhalten, wenn Sie es als eine C++ Datei mit g ++ kompiliert haben.

Der C-Standard sagt:

Ein Array von Zeichentyp kann durch eine Zeichenfolge Literal oder UTF-8 Stringliteral, gegebenenfalls in geschweiften Klammern initialisiert werden. Aufeinanderfolgende Bytes des Zeichenfolgenliterals (einschließlich des abschließenden Nullzeichens , wenn Platz vorhanden ist oder wenn das Array eine unbekannte Größe hat) initialisieren Sie die Elemente des Arrays.

[...]

BEISPIEL 8

Die Erklärung

char s[] = "abc", t[3] = "abc"; 

'glatt' 'definiert' char Array-Objekte s und t deren Elemente mit Zeichenkette initialisiert Literale. Diese Erklärung zu

char s[] = { 'a', 'b', 'c', '\0' }, 
    t[] = { 'a', 'b', 'c' }; 

identisch ist (Abschnitt 6.7.9 der C11 draft standard, Wortlaut in endgültigem Standard könnte anders sein.)

Das bedeutet, dass es vollkommen richtig ist die Kündigung fallen Zeichen, wenn das Array keinen Platz dafür hat. Es ist vielleicht unerwartet, aber es ist genau so, wie die Sprache funktionieren soll, und ein (zumindest für mich) bekanntes Feature.

Im Gegenteil, der C++ Standard sagt:

Es gibt nicht mehr initializers sein darf als Array-Elemente sind.

Beispiel:

char cv[4] = "asdf"; // error 

ist schlecht ausgebildet, da es keinen Platz für die implizierte Hinter '\ 0'.

(. 8.5.2 des C++ 2011 Entwurf n3242)

+1

Unwind: Wenn vom Standard erlaubt, irgendeine Idee, warum VC++ dies als Fehler kennzeichnet? –

+0

Unwind: Wenn das Nullzeichen aus dem Literal (in der obigen Initialisierung) gelöscht wurde, ist es sicher, eine solche Zeichenfolge in Funktionen wie Strlen zu verwenden? –

+1

@Ashwin Nicht sicher über Visual Studio, vielleicht kompiliert es als C++, für die die Regeln * möglicherweise * anders sein? Nein, es ist nicht sicher, da 'strlen()' eine richtige 0-terminierte Zeichenkette benötigt. – unwind

3

Die preffered Weise ein Zeichenfolgenliteral zu erklären ist in der Regel:

char a[] = "Network"; 
    printf("size of a: %d\n", sizeof a); // The compiler 'knows' the size of a. 
    // this prints '8' 

der Compiler Figur Lass es raus. Es ist mühsam, die Array-Größe manuell anzugeben und sie mit der tatsächlichen Länge des String-Literals synchron zu halten ...

Also denke ich, dass GCC nicht wirklich mit mehr als einer Warnung beschäftigt ist.

+3

-1: Sie haben die Frage nicht beantwortet. –

3

In den frühen Tagen von C und Unix, Speicher und Festplatte waren klein, so nicht das NULL-Byte am Ende der Zeichenfolge speichern war eigentlich eine Technik, die verwendet wurde. Wenn die Zeichenfolgenvariable sieben Zeichen lang ist, können Sie eine siebenstellige Zeichenfolge darin speichern, und da sieben die maximale Länge war, wussten Sie, dass die Zeichenfolge dort endete, auch ohne das Abschlusszeichen. Deshalb funktioniert strncpy so, wie es funktioniert.

0

Während unwind's answer erklärt, warum gcc nicht davor warnt, es sagt nicht, was Sie dagegen tun können.

gcc s‘-Wc++-compat Warnung Option wird diese besondere Ausgabe mit der Meldung erkennen:

foo.c: In function ‘main’: 
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat] 

, dass die einzige Möglichkeit ist, die gcc verursacht dieses Problem zu warnen. Sie können ein kurzes Skript schreiben, um die Warnoptionen schnell aus der Manpage gcc zu holen, versuchen Sie es mit jedem zu kompilieren und sehen Sie, ob es sich beschwert.

$ time for F in $(man gcc | grep -o -- '-W[^= ]*') 
    do if gcc -c "${F}" foo.c |& grep :3 >& /dev/null; then 
     echo "${F}"; gcc -c "${F}" foo.c 
    fi 
    done 
man gcc | grep -o -- '-W[^= ]*') 
man gcc | grep -o -- '-W[^= ]*' 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wc++-compat 
foo.c: In function ‘main’: 
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat] 
-Wunused 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused-variable 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wtraditional 
foo.c: In function ‘main’: 
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused-variable 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused-variable 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wunused 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wtraditional 
foo.c: In function ‘main’: 
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] 
-Wtraditional 
foo.c: In function ‘main’: 
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] 
-Wc++-compat 
foo.c: In function ‘main’: 
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wtraditional 
foo.c: In function ‘main’: 
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wall 
foo.c: In function ‘main’: 
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable] 
-Wtraditional 
foo.c: In function ‘main’: 
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] 

real 0m26.399s 
user 0m5.128s 
sys 0m15.329s 

Im Allgemeinen ist ein lint -ähnliche Tool wie splint werden Sie über alle Arten von möglichen Problemen warnen. In diesem Fall wird es sagen:

foo.c:3:17: String literal with 8 characters is assigned to char [7] (no room 
       for null terminator): "Network" 
    A string literal is assigned to a char array that is not big enough to hold 
    the null terminator. (Use -stringliteralnoroom to inhibit warning) 
foo.c:3:10: Variable a declared but not used