2010-10-20 6 views
9

Ich schreibe ein C-Programm, das mit allen wichtigen Compilern kompiliert werden soll. Derzeit entwickle ich GCC auf einem Linux-Rechner und kompiliere auf MSVC, bevor ich den Code beginne. Um das Cross-Compilieren zu erleichtern, kompiliere ich mit -ansi und -pedantic Flags. Dies funktionierte gut, bis ich anfing, snprintf zu verwenden, das in C89 Standard nicht verfügbar ist. GCC kann das ohne den -ansi Schalter kompilieren aber MSVC wird immer fehlschlagen, da es C99 Unterstützung nicht unterstützt.Verwendung von snprintf in einer plattformübergreifenden Anwendung

Also habe ich so etwas wie,

#ifdef WIN32 
#define snprintf sprintf_s 
#endif 

Das funktioniert gut, weil snprintf und sprintf_s gleiche Unterschriften haben. Ich frage mich, ist das der richtige Ansatz?

+1

ist nicht "snprintf" Standard für alle C in jeder Plattform? –

+2

nein. 'snprintf' ist Teil des C99-Standards. MSVC hat keine C99-Implementierung. –

+3

'sprintf_s' ist nicht gleichwertig. 'snprintf' gibt die Anzahl der Zeichen zurück, die geschrieben worden wären, während' sprintf_s' -1 beim Abschneiden zurückgibt. Siehe [diese Diskussion] (http://social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/2b339bdf-7ab1-4a08-bf7e-e9293801455b/). –

Antwort

-5

Nr. Ihr Ansatz ist zum Scheitern verurteilt.

sqrt und cos haben den gleichen Prototyp. Denkst du, du kannst sie in einem Programm austauschen und das gleiche Verhalten von vor/nach der Änderung erhalten?


Sie sollten wahrscheinlich Ihre eigenen snprintf schreiben, oder eine Implementierung aus dem Internet herunterladen (google is your friend) und verwenden, die sowohl in Linux und Windows.

+0

danke. Ich werde eine benutzerdefinierte Implementierung versuchen. –

+7

Diese Antwort ist nicht hilfreich. Anders gefragt: Da snprintf und sprintf_s dieselbe Signatur haben, haben sie auch die gleiche Funktionalität? Deine Antwort sagt grundsätzlich: Ich weiß es nicht. –

14

Ich fand this auf die Verwendung von _snprintf() als eine Alternative, und die Fehler beteiligt, wenn der Puffer Überlaufschutz tatsächlich auslöst. Von dem, was ich auf einen kurzen Blick sehen konnte, gelten ähnliche Vorbehalte für sprintf_s.

Können Sie das Problem sehen? In der Linux-Version ist die Ausgabe immer null-terminiert. In MSVC ist es nicht.

Noch subtiler ist der Unterschied zwischen dem size Parameter in Linux und count Parameter in MSVC. Ersteres ist die Größe des Ausgabepuffers einschließlich des abschließenden Nullwerts und letzteres ist die maximale Anzahl der zu speichernden Zeichen, die den abschließenden Nullwert ausschließt.

Oh, und vergessen Sie nicht, eine Mail an Microsoft zu senden, in der Sie aufgefordert werden, die aktuellen Sprachstandards zu unterstützen. (Ich weiß, dass sie bereits angekündigt haben, dass sie keinen Plan haben, C99 zu unterstützen, aber bugger sie sowieso. Sie verdienen es.)

Fazit, wenn Sie es wirklich sicher spielen wollen, müssen Sie Ihre eigenen snprintf() (ein Wrapper um _snprintf() oder sprintf_s() ihr Nicht-Standard-Verhalten für MSVC zu fangen.

+0

Ich empfehle einen "Contract Wrapper" um existierende Implementierungen (wie in der Antwort erwähnt) statt irgendeinen Code von Drittanbietern in Ihr Projekt zu ziehen (wie von pmg vorgeschlagen). Eine vollständige '* printf()' Implementierung ist ziemlich groß. – DevSolar

+0

Tatsächlich kann eine vollständige 'printf'-Implementierung sehr klein sein. Normalerweise würde ich mit Ihrem Prinzip einverstanden sein, zerbrochene Implementierungen zu umhüllen, anstatt sie neu zu implementieren, aber da Windows '' printf 'auch einige kaputte Dinge enthält, die Sie nicht einfach wegpacken können (wie die Rückwärtsinterpretation von '% s' und'% ls') in den großen Varianten), frage ich mich, ob es einfach die beste Lösung wäre, sie zu ersetzen. –

+0

Eigentlich eine andere Sache, die Sie zur gleichen Zeit reparieren können, ist der ungenaue Fließkommadruck von MS. –

0

die vollständigste Antwort (Sie können sich verbessern, wenn Sie möchten), setzen die in einen Aufkleber

#if __PLATFORM_WIN_ZERO_STANDARD__ 

    static inline 
    int LIBSYS_SNPRINTF(char * str, size_t size, const char * format, ...) 
    { 
     int retval; 
     va_list ap; 
     va_start(ap, format); 
     retval = _vsnprintf(str, size, format, ap); 
     va_end(ap); 
     return retval; 
    } 

    static inline 
    int LIBSYS_VASPRINTF(char **ret, char * format, va_list ap) 
    { 
     int wanted = vsnprintf(*ret = NULL, 0, format, ap); 
     if((wanted > 0) && ((*ret = LIBSYS_MALLOC(1 + wanted)) != NULL)) { 
      return vsprintf(*ret, format, ap); 
     } 
     return wanted; 
    } 

    static inline 
    int LIBSYS_ASPRINTF(char **ret, char * format, ...) 
    { 
     int retval; 
     va_list ap; 
     va_start(ap, format); 
     retval = LIBSYS_VASPRINTF(ret, format, ap); 
     va_end(ap); 
     return retval; 
    } 

#else 
    #define LIBSYS_SNPRINTF snprintf 
    #define LIBSYS_VASPRINTF vasprintf 
    #define LIBSYS_ASPRINTF asprintf 
#endif 
+0

Ziemlich sicher "inline" und variadic Funktionen sind C99 Funktionen. – Thomas

+3

@Thomas 'printf()' ist eine variadische Funktion. Es ist schon eine Weile her ... Also nein, das ist kein C99-Zusatz. – unwind

9

Ihr Vorschlag arbeiten können, wenn Sie vorsichtig sind.Das Problem ist, dass beide Funktion etwas anders verhalten, wenn das nicht ein Problem für Sie, Sie sind gut zu gehen, denken anders über eine Wrapper-Funktion:

Unterschiede zwischen MSVCs _snprintf und offiziellem C99 (gcc, Klirren) snprintf:

Rückgabewert:

  • MSVC: return -1, wenn Puffergröße nicht genug, um alles zu schreiben
  • GCC (ohne abschließende null!): return Anzahl von Zeichen, die groß, wenn Puffer geschrieben worden wäre, genug

Geschriebene Bytes:

  • MSVC: Schreiben Sie so viel wie möglich, schreiben Sie nicht NULL am Ende, wenn kein Raum
  • GCC links: schreiben, so viel wie möglich, immer schreiben NULL beendet (Ausnahme : buffer_size = 0)

Interessante %n Subtilität: Wenn Sie %n in Ihrem Code verwenden, verlässt MSVC es unitialized! Wenn es aufhört zu analysieren, weil die Puffergröße zu klein ist, schreibt GCC immer die Anzahl der Bytes, die geschrieben worden wären, wenn der Puffer groß genug gewesen wäre.

So wäre mein Vorschlag, Ihre eigene Wrapper-Funktion schreiben mysnprintfvsnprintf/_vsnprintf mit den gleichen Rückgabewerte und schreibt das gleiche Bytes auf beiden Plattformen gibt (Vorsicht: %n ist schwieriger zu beheben).

+0

Es ist ein wenig einfacher, auf die nächste Ebene zu wickeln. Ich musste dies vor einiger Zeit tun, und der Ansatz, den ich benutzte, hatte eine innere Funktion, die einen Puffer einer bestimmten Größe zuweist() und den Aufruf vsnprintf; Wenn das Ergebnis passt, wurde es in einen anderen Speicher entsorgt, und die Funktion würde OK zurückgeben. Wenn nicht, würde die Funktion einen 'try again with specified bigger buffer size' Code zurückgeben. Für gcc war die Wiederversuchsgröße aus der vsnprintf-Rückgabe bekannt, und für MSVC würde sie basierend auf einigen Heuristiken einfach weiter zunehmen. – greggo

1

Sie könnten die NUL-Spezialdatei für MSVC öffnen und darauf schreiben. Es sagt Ihnen immer, wie viele Bytes benötigt werden, und schreibt nichts. Wie folgt:

int main (int argc, char* argv[]) { 
    FILE* outfile = fopen("nul", "wb"); 
    int written; 

    if(outfile == NULL) { 
    fputs ("could not open 'nul'", stderr); 
    } 
    else { 
    written = fprintf(outfile, "redirect to /dev/null"); 
    fclose(outfile); 
    fprintf(stdout, "didn't write %d characters", written); 
    } 

    return 0; 
} 

Sie sollten dann wissen, wie viele Bytes zuzuweisen, um sprintf erfolgreich zu verwenden.