2009-10-07 13 views
5

C++ in MS Visual Studio 2008. Warnstufe 4 plus eine Ladung zusätzlicher Warnungen aktiviert. Ich würde erwarten, dass dies eine Warnung geben mindestens, aber wahrscheinlicher ein Compilerfehler?Warum/wie kompiliert sich das?

Funktionsdeklaration ist wie folgt:

int printfLikeFunction(
    const int    bufferLength, 
    char * const   buffer, 
    const char * const  format, 
    ...); 

Nutzungscode - gibt es ein Tippfehler: Obwohl die ARRAY_SIZE von Outputgeben wird in, Output selbst ist nicht - sicher sollte dies nicht kompilieren:

printfLikeFunction(ARRAY_SIZE(outputBuffer), "Format: %s, %s", arg1, arg2); 

Dies ist eindeutig falsch und ein Fehler wurde gemacht. Allerdings hätte der Compiler es fangen sollen! Der Puffer-Parameter sollte ein char-Zeiger sein, und es wird ein String-Literal übergeben, das ein const char-pointer ist. Dies muss ein Fehler sein. (arg1 und arg2 sind auch (möglicherweise const) char Zeiger, so zufällig die Deklaration übereinstimmt auch ohne outputBuffer an der richtigen Stelle).

Zur Laufzeit stürzt dieser Code ab, wenn er versucht, in das Zeichenfolgenliteral zu schreiben. Keine Überraschung, ich verstehe einfach nicht, wie es kompilieren durfte.

(Dies ist jedoch vermutlich der Grund, warum sprintf_s die Puffer- und Größenparameter in einer anderen Reihenfolge als diese Funktion hat - sie macht solche Fehler eindeutig fehl.)

Antwort

9

C++ hat eine spezielle Lücke für Zeichenfolgenliterale für die Kompatibilität mit C-style-Code const. Obwohl Zeichenfolgenliterale Arrays von const char sind, können sie in einen Zeiger auf nicht-const Zeichen konvertiert werden.

Paraphrasierung 4.2/2 [conv.array]: Ein 'schmales' String-Literal kann in einen rvalue vom Typ pointer to non konvertiert werden constchar. Die Konvertierung wird nur berücksichtigt, wenn ein expliziter Zieltyp vorhanden ist (z. B. ein Funktionsparameter) und nicht, wenn eine allgemeine lvalue to rvalue-Konvertierung erforderlich ist.

Diese Konvertierung ist veraltet, aber immer noch verfügbar. Beachten Sie, dass während die Konvertierung ermöglicht, dass das Literal in einen Zeiger auf einen Nicht-const char-Typ konvertiert wird, es trotzdem ein undefiniertes Verhalten aufrufen würde, um zu versuchen, alle Zeichen im Zeichenfolgenliteral durch diesen Zeiger zu ändern.

+0

Wow. Was für eine verrückte Entscheidung! Aber, ja, ich habe gerade den Standard überprüft und Sie haben absolut Recht. Nicht zu fassen! –

+0

Sie würden hoffen, dass Visual Studio eine Warnung ausgeben würde, wenn veraltete und verrückte Funktionen des Standards verwendet werden. –

+0

Ja ... auch wenn "Verrücktheit" subjektiv ist, veraltet ist das nicht, so dass es mich erstaunt, dass dies keine Warnung hervorbrachte. –

2

Ich erinnere mich, dass der Compiler eine Option hat, die steuert, wie Zeichenfolgenliterale behandelt werden. Standardmäßig werden sie wie char * behandelt, um nicht eine ganze Menge vorhandenen nicht-konstensicheren Codes zu beschädigen, aber Sie können sie ändern, um sie als const char * zu behandeln. Dies kann dazu beitragen, den Fehler auszulösen, nach dem Sie suchen.

(später) Ich habe im Moment keinen Microsoft Compiler zur Hand, aber wenn ich mir den Verweis auf MSDN ansehe, kann ich keine solche Option finden. Ich denke vielleicht an GCC, die eine solche Option hat.

+0

Blick auf Charles 'Antwort (http://stackoverflow.com/questions/1530330/why-how-does-this-compile/1530469#1530469). Es gibt eine veraltete Konvertierung von einem beliebigen Zeichenfolgenliteral zu 'char *', das das Kompilieren von altem Code ermöglicht. IMO, das ist ein PITA, weil es ermöglicht, dass Code wie oben ohne Fehler kompiliert wird, aber so ist es. – sbi

+0

Interessant, danke! –

1

int printfLikeFunction ( const int bufferLength, const char * Puffer, const char * const Format, ...);

Der Pufferparameter wird als char * const angegeben. Er schützt also nur die zu ändernde Pufferadresse, nicht den Pufferinhalt.

Um zu vermeiden, dass Puffer in geschrieben wird, müssen Sie es als const char * const wie das Format deklarieren.

Der Compiler ermöglicht das Schreiben in Puffer, da Sie es als char * deklarieren. Der Modifikator const postfix verhindert nur, dass der Pufferwert in Ihrer Funktion neu zugewiesen wird.

siehe http://jriddell.org/const-in-cpp.html const Modifikator Auswirkungen auf Variablen

+1

Ich denke, das Problem ist, dass das OP vergessen hat, den 'buffer' Parameter zu übergeben, und was der' format' Parameter sein soll, wird als 'buffer' genommen. – dave4420

+0

Ich nehme an, dass der Puffer geändert werden soll, hier wird der Ausgabe-String geschrieben. Das Problem hierbei ist, dass das String-Literal als char * behandelt wird, während es als const char * behandelt werden sollte. –

+0

I der Puffer geändert werden soll und eine Literalkonstante als Pufferparameterwert übergeben wird, kommt es zu einer Speicherbeschädigung, da der Compiler das Literal an eine feste Adresse im Speicher setzt. – dweeves

1

Stringliterale in C sind const char Zeiger (char*const), nicht const Zeiger anzuzeigen Zeichen const (const char* const).

C++ folgte ursprünglich C-Verwendung, aber dann wurde der ANSI C++ - Standard irgendwann geändert (ich weiß nicht wann), um sie zu const char* const zu machen.Microsoft-Produkte tendieren seit jeher dazu, die Rückwärtskompatibilität mit früheren Versionen gegenüber der Kompatibilität zu bewerten - möglicherweise gibt es eine Compileroption, um das "neue" Verhalten zu erzwingen, aber da die Beispiele für Zeichenfolgenliterale in MSDN nicht konstant sind, bezweifle ich das.

+2

In C++ haben schmale Zeichenfolgenliterale den Typ const char [] ', der implizit in const char *' konvertiert werden kann. Wie Charles erklärte, gibt es jedoch eine veraltete Konvertierung in 'char *'. – sbi

Verwandte Themen