2009-12-29 7 views
18

Ich habe diese Art von Konvention schon oft in meinem Code in der Vergangenheit verwendet:Können die Eingabe- und Ausgabezeichenfolgen für sprintf() gleich sein?

strcpy (cTmpA, "hello"); 
sprintf (cTmpA, "%s world", cTmpA); 

Kürzlich habe ich meinen Legacy-C-Compiler auf Visual Studio 2005 umgestellt und festgestellt, dass ich aus dem obigen Code eine verstümmelte Zeichenfolge habe. Es ist mir dann aufgefallen, dass das Verhalten von sprintf() vielleicht nicht fest definiert ist, wo eine der Eingaben mit der Ausgabe übereinstimmt.

Ist der obige Code gültig K & R C? Wenn nicht, wie finde ich alle Stellen in meinem Code, an denen diese Art von Aufruf von sprintf() aufgetreten ist?

+2

Duplikat von http://StackOverflow.com/Questions/1283354/is-SprintfBuffer-S-Buffer-Safe – Alon

Antwort

18

Während es gültig ist K & R C, möchten Sie wahrscheinlich lieber wissen, ob es POSIX gültig ist - siehe sprintf Specification. Wir lesen:

Wenn das Kopieren zwischen Objekten stattfindet, die sich als Ergebnis eines Aufrufs von sprintf() oder snprintf() überlappen, sind die Ergebnisse nicht definiert.

8

Die meisten Implementierungen von sprintf() kopieren nicht die Formatzeichenfolge und verwenden stattdessen einen Zeiger innerhalb der übergebenen Zeichenfolge. Wenn Format und Ausgabe auf denselben Speicher verweisen, führt dies zu ungeraden Ergebnissen.

Und Sie sollten wirklich snprintf() verwenden, die Sie vor Pufferüberläufen schützt.

Um alle Anrufe zu finden, setzen Sie #define sprintf +++ in einen gemeinsamen Header suchen und alle Quellen neu kompilieren. Das sollte Ihnen eine Liste von Fehlern zusammen mit dem Dateiname und den Zeilennummern geben :) Oder verwenden Sie die rekursive Suche nach Ihrer IDE.

Wenn Sie diese Liste trimmen wollen bis auf die, wo Sie den gleichen Zeiger für beide Argumente verwenden, verwenden Sie das Makro:

#define sprintf(output,format,...) check_sprintf(__FILE__,__LINE__,output,format,....) 

Beachten Sie, dass nicht alle Compiler-Unterstützung von Makros mit varargs. definieren Sie dann eine neue Funktion check_sprintf:

int check_sprintf (char*filename,int line,char*output,char*format,...) { 
    va_list args; 
    int len; 

    if(output==format) { 
     fprintf(stderr, 
      "Output and format are the same at %s:%d", filename, line); 
      abort(); 
    } 

    va_start (args, format); 
    len = vsprintf (output, format, args); 
    va_end (args); 

    return len; 
} 

[EDIT] Ich habe gerade gesehen, dass Sie über den Ausgang und das erste Argument sprechen. Sie können den Code von oben wiederverwenden und va_arg() aufrufen, um das erste Argument zu erhalten und das im Vergleich zu verwenden.

+0

Gute Idee bei der Verwendung von Makroersatz, um zu erkennen, wo dies auftritt. Ich dachte darüber nach, konnte mir aber keine Möglichkeit vorstellen, ein Makro zu erstellen, das nur dann zu Fehlern bei der Kompilierung führte, wenn die Ausgabe auch eine der Eingaben ist. Das Problem bei der Neudefinition von sprintf() ist, dass dies nur zur Laufzeit angezeigt wird, während mein Projekt groß genug ist, dass ich niemals alle Code-Pfade ohne irgendeine Art von automatisierten Komponententests finden würde. – Piers

+0

Im Laufe der Zeit werden Sie alle Orte finden, weil das Programm stark abstürzt (anstatt stumme Daten zu erzeugen). Beginnen Sie also jetzt mit Ihren Komponententests und reparieren Sie so viele Orte wie möglich und beheben Sie den Rest, sobald die Fehlerberichte eintreffen. –

Verwandte Themen