Typüberprüfung bei FILE* als Parameter in va_list

09/10/2009 - 23:43 von Andreas Eibach | Report spam
Hi,

ich hab mir hier eine Log-Routine gebastelt, die auch printf() Attribute
versteht.
Da geht man natürlich am besten mit va_list zu Werke, da es einmal z. b. 2,
und einmal auch 4 Attribute sein können, was man vorher nie weiß.
Sieht so aus:

void logMe (FILE* ofile, const char* fmtStr, ...)
{
va_list arg_p;

va_start (arg_p, fmtStr); /* init. Argumentenliste NACH fmtStr */
vfprintf (ofile, fmtStr, arg_p);
va_end (arg_p);
fputs ("", ofile);}

char strEins[10];
char strZwei[10];
strcpy (strEins, "eins");
strcpy (strZwei, "zwei");

/* h_logfile = vordefiniertes Filehandle, Öffnen war erfolgreich */
logMe (h_logfile, "Blubb %s %s", strEins, strZwei);

-
Toll. Nix Aufregendes.
Nehmen wir aber mal an, logMe() befànde sich in einer kleinen Toolsammlung,
die würde ich unter GPL stellen.
Jetzt geht ein Anfànger her und programmiert __versehentlich__ analog:

logMe ( "Blubb %s %s", strHinz, strKunz);

Die Resultate sind unvorhersehbar!!
Im "isolierten" Test löste das Programm so einen GPF aus.
(Natürlich weiß ich, dass bei vernünftig eingestelltem Warnlevel da
Warnungen über "pasisng argument X ..." etc. kommen. Nehmen wir aber mal an,
der Jungprogrammierer sieht auch die grade irgendwie nicht...)

FRAGE:
Wie kann ich so etwas sauber abfangen?

(nich schlagen) Mit den internen Attributen der struct _iobuf in stdio.h hab
ich schon herumexperimentiert, da hab ich aber leider kein Kriterium
gefunden wie man das sinnvoll abfangen kann und z. B. vorher einen exit(0)
auslösen kann, bevor Schlimmeres passiert.
Abfrage von ofile auf NULL ging auch nicht.

Gibt's da noch einen anderen Trick?

-Andreas
 

Lesen sie die antworten

#1 Michael Schumacher
10/10/2009 - 15:07 | Warnen spam
Andreas Eibach wrote:

ich hab mir hier eine Log-Routine gebastelt, die auch printf() Attribute
versteht.
Da geht man natürlich am besten mit va_list zu Werke, da es einmal z. b.
2, und einmal auch 4 Attribute sein können, was man vorher nie weiß.
Sieht so aus:

void logMe (FILE* ofile, const char* fmtStr, ...)
{
va_list arg_p;

va_start (arg_p, fmtStr); /* init. Argumentenliste NACH fmtStr */
vfprintf (ofile, fmtStr, arg_p);
va_end (arg_p);
fputs ("", ofile);}

char strEins[10];
char strZwei[10];
strcpy (strEins, "eins");
strcpy (strZwei, "zwei");

/* h_logfile = vordefiniertes Filehandle, Öffnen war erfolgreich */
logMe (h_logfile, "Blubb %s %s", strEins, strZwei);

-
Toll. Nix Aufregendes.
Nehmen wir aber mal an, logMe() befànde sich in einer kleinen
Toolsammlung, die würde ich unter GPL stellen.
Jetzt geht ein Anfànger her und programmiert __versehentlich__ analog:

logMe ( "Blubb %s %s", strHinz, strKunz);

Die Resultate sind unvorhersehbar!!



Doch, sind sie: sind logMe() und der o.a. Testcode im selben Modul
untergebracht, wird es wie gewünscht funktionieren, aber:

Im "isolierten" Test löste das Programm so einen GPF aus.
(Natürlich weiß ich, dass bei vernünftig eingestelltem Warnlevel da
Warnungen über "pasisng argument X ..." etc. kommen.



In diesem Beispiel nicht wirklich (oder sagen wir besser: nicht
notwendigerweise; s.u.).

Nehmen wir aber mal
an, der Jungprogrammierer sieht auch die grade irgendwie nicht...)

FRAGE:
Wie kann ich so etwas sauber abfangen?



Indem man den korrekten Prototyp von logMe() in eine Header-Datei
schreibt, und diese von allen Modulen #includen làßt, die diese
Funktion benutzen. Dadurch weiß der Compiler, daß er statt
"strEins" und "strZwei" "&strEins[0]" und "&strZwei[0]" übergeben
muß (ohne Header bzw. Prototyp weiß er das nur, wenn die Funktion
im selben Modul definiert ist, in der sie auch aufgerufen wird). Der
vielleicht nicht ganz offensichtliche Unterschied zwischen "strEins"
und "&strEins[0]", die ja beide die Adresse des ersten Zeichens des
Strings "strEins" angeben, besteht darin, daß ersteres die direkte,
letzteres die indirekte Adresse darstellt, oder anders ausgedrückt:
ersteres ist die String-Startadresse im Speicher, letzteres ein
Zeiger auf diese Adresse (sie belegt also zusàtzlichen Speicherplatz).
Unterlàßt der Compiler diese implizite Konvertierung, interpretiert
logMe() die ersten Zeichen in "strEins" als Adresse, und versucht
indirekt darüber zuzugreifen -- das führt dann entweder zu falschen
Ergebnissen oder zu einem Hardware-Trap.

(nich schlagen) Mit den internen Attributen der struct _iobuf in stdio.h
hab ich schon herumexperimentiert, da hab ich aber leider kein Kriterium
gefunden wie man das sinnvoll abfangen kann und z. B. vorher einen exit(0)
auslösen kann, bevor Schlimmeres passiert.
Abfrage von ofile auf NULL ging auch nicht.

Gibt's da noch einen anderen Trick?



"Tricks" sind für saubere Programmierung normalerweise nicht notwendig;
aber der GNU-C-Compiler bietet noch eine interessante Erweiterung über
den "__attribute__()"-Qualifier. Damit kann man z.B. angeben, daß eine
Funktion ihre Argumente wie "printf()" verarbeitet; der Compiler prüft
dann automatisch, ob Anzahl und Typ der Formatzeichen mit denen der
angegebenen Argumente übereinstimmen, und generiert ggf. Warnungen oder
auch Fehlermeldungen. Nettes Feature! :-)


mike

Ähnliche fragen