printf, wprintf, %s, %S, %ls, char* und wchar*: Fehler, die nicht durch eine Compiler-Warnung angekündigt werden?

Lesezeit: 9 Minuten

Benutzer-Avatar
Antonio

Ich habe folgenden Code ausprobiert:

wprintf(L"1 %s\n","some string"); //Good
wprintf(L"2 %s\n",L"some string"); //Not good -> print only first character of the string
printf("3 %s\n","some string"); //Good
//printf("4 %s\n",L"some string"); //Doesn't compile
printf("\n");
wprintf(L"1 %S\n","some string"); //Not good -> print some funny stuff
wprintf(L"2 %S\n",L"some string"); //Good
//printf("3 %S\n","some string"); //Doesn't compile
printf("4 %S\n",L"some string");  //Good

Und ich bekomme folgende Ausgabe:

1 some string
2 s
3 some string

1 g1 %s

2 some string
4 some string

Also: es scheint, dass beides wprintf und printf können sowohl ein char* als auch ein wchar* korrekt ausgeben, aber nur, wenn der exakte Bezeichner verwendet wird. Wenn der falsche Bezeichner verwendet wird, erhalten Sie möglicherweise keinen Kompilierungsfehler (und keine Warnung!) und enden mit falschem Verhalten. Erleben Sie das gleiche Verhalten?

Hinweis: Dies wurde unter Windows getestet, mit MinGW kompiliert und g++ 4.7.2 (ich werde gcc später überprüfen)

Edit: Ich habe auch %ls ausprobiert (Ergebnis steht in den Kommentaren)

printf("\n");
wprintf(L"1 %ls\n","some string"); //Not good -> print funny stuff
wprintf(L"2 %ls\n",L"some string"); //Good
// printf("3 %ls\n","some string"); //Doesn't compile
printf("4 %ls\n",L"some string");  //Good

  • Wenn Sie den falschen Bezeichner verwenden, erhalten Sie undefiniertes Verhalten.

    – zwischenjay

    17. Juli 2013 um 13:22 Uhr

  • Es ist immer so: Wenn man es richtig macht, funktioniert es. Wenn du es falsch machst, geht es nicht.

    – glglgl

    17. Juli 2013 um 13:22 Uhr

  • Dies scheint zu entsprechen SUSv2. Entsprechende C99-konforme Formatbezeichner wären %s und %ls.

    – David Förster

    17. Juli 2013 um 13:23 Uhr


  • @interjay Warum gibt es keine Warnung oder einen Compilerfehler?

    – Antonio

    17. Juli 2013 um 13:25 Uhr

  • @Antonio Versuchen Sie, mit zu kompilieren -Wall Flagge.

    – zwischenjay

    17. Juli 2013 um 13:27 Uhr

Ich vermute, dass GCC (mingw) benutzerdefinierten Code hat, um die Prüfungen für die Weite zu deaktivieren printf Funktionen unter Windows. Dies liegt daran, dass Microsofts eigene Implementierung (MSVCRT) ist schlecht falsch und hat %s und %ls rückwärts für die Weite printf Funktionen; Da GCC nicht sicher sein kann, ob Sie mit der fehlerhaften MS-Implementierung oder einer korrigierten verknüpfen, ist das am wenigsten aufdringliche, was es tun kann, einfach die Warnung abzuschalten.

  • es ist nicht sehr falsch … das ist wie zu sagen, was Sie getan haben, ist falsch, weil Sie Ihrer eigenen Spezifikation gefolgt sind und nicht der eines anderen (ANSI)

    – jbu

    15. Mai 2014 um 18:57 Uhr

  • @jbu: Es bedeutet, dass die Sprache, die sie unterstützen, nicht C ist, sondern eine zufällige C-ähnliche Sprache, die Microsoft entwickelt hat. Wenn Sie also sagen, dass Sie C unterstützen, ist das nicht nur anders, es ist so falsch.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    10. September 2014 um 15:29 Uhr

  • @R .. könnten Sie auf eine Referenz verweisen, die besagt, dass Microsoft selbst behauptet hat, einen ANSI C-Standard vollständig einzuhalten? Die C-Sprache allein ohne Bezugnahme auf einen Standard hat überhaupt keine Bedeutung.

    – Mathias

    29. Januar 2017 um 12:49 Uhr

  • @Matthias Colin Robertson von Microsoft bestätigte auf GitHub, dass MSVC einen C89/90-kompatiblen Compiler mit einigen Funktionen aus neueren Versionen enthält. Aber egal, ich bin überrascht, dass du das vorschlägst %ls (buchstäblich langer/breiter String) und %s sollten ihre Bedeutungen vertauscht haben irgendein Compiler.

    – Benjamin Crawford Strg-Alt-Tut

    8. Juli 2020 um 16:55 Uhr

  • @R..GitHubSTOPHELPINGICE Überprüfen Sie die Erwähnung, ich habe Matthias geantwortet.

    – Benjamin Crawford Strg-Alt-Tut

    9. Juli 2020 um 13:44 Uhr

Die Formatangaben sind wichtig: “%s” sagt aus, dass die nächste Zeichenfolge eine schmale Zeichenfolge ist (“ascii” und typischerweise 8 Bit pro Zeichen). “%S” bedeutet breite Zeichenkette. Das Mischen der beiden ergibt ein “undefiniertes Verhalten”, das das Drucken von Müll, nur ein Zeichen oder nichts beinhaltet.

Ein Zeichen wird gedruckt, weil breite Zeichen beispielsweise 16 Bit breit sind und das erste Byte nicht Null ist, gefolgt von einem Null-Byte -> Ende der Zeichenfolge in schmalen Zeichenfolgen. Dies hängt von der Byte-Reihenfolge ab. In einer “Big-Endian” -Maschine erhalten Sie überhaupt keine Zeichenfolge, da das erste Byte Null ist und das nächste Byte einen Wert ungleich Null enthält.

  • Warum wird beim Kompilieren keine Warnung ausgegeben?

    – Antonio

    17. Juli 2013 um 13:28 Uhr

  • Hängt vom Compiler ab – gcc mit geeigneter Warnstufe sollte ausreichen.

    – Mats Petersson

    17. Juli 2013 um 13:29 Uhr

  • Ich habe alle Warnungen aktiviert, aber ich kompiliere mit g++ (es ist eine komplizierte Geschichte), ich werde später prüfen, ob es wichtig ist …

    – Antonio

    17. Juli 2013 um 13:42 Uhr

  • Funktioniert bei mir: “text.cpp:5:24: Warnung: Format ‘%s’ erwartet Argument vom Typ ‘char*’, aber Argument 2 hat den Typ ‘const wchar_t*’ [-Wformat]”

    – Mats Petersson

    17. Juli 2013 um 13:44 Uhr

  • Wenn Sie jedoch auf einem Windows-System kompilieren, kann es sein, dass die Funktion „wprintf“ in der Header-Datei nicht als „dies ist eine Funktion vom Typ printf“ gekennzeichnet ist. In diesem Fall weiß gcc nicht, dass sie interpretieren soll die Eingabe als Formatzeichenfolge (Sie möchten keine Warnung erhalten strcpy(blah, "%s"); Zum Beispiel muss es wissen, was a ist printf style-Funktion aus der Header-Datei).

    – Mats Petersson

    17. Juli 2013 um 13:46 Uhr

Für s:
Gibt bei Verwendung mit printf-Funktionen eine Einzelbyte- oder Multibyte-Zeichenfolge an; Gibt bei Verwendung mit wprintf-Funktionen eine Zeichenfolge mit breiten Zeichen an. Zeichen werden bis zum ersten Nullzeichen oder bis zum Erreichen des Genauigkeitswerts angezeigt.

Für S:
Gibt bei Verwendung mit printf-Funktionen eine Zeichenfolge mit breiten Zeichen an; Gibt bei Verwendung mit wprintf-Funktionen eine Einzelbyte- oder Multibyte-Zeichenfolge an. Zeichen werden bis zum ersten Nullzeichen oder bis zum Erreichen des Genauigkeitswerts angezeigt.

Auf einer Unix-ähnlichen Plattform haben s und S die gleiche Bedeutung wie die Windows-Plattform.

Bezug:
https://msdn.microsoft.com/en-us/library/hf4y5e3w.aspx

  • %S findet sich nicht im C-Standard (!): open-std.org/jtc1/sc22/wg14/www/docs/n2310.pdf und ist nur eine Erweiterung, die Compiler implementieren … Es sollte davon abgeraten werden, sie zu verwenden.

    – 71GA

    6. Februar 2020 um 8:59 Uhr


  • Im zweiten Fall, den das OP gepostet hat, wird wprintf mit %s und einer wchar-Zeichenfolge verwendet, nicht wahr? Es wird aber nur das erste Zeichen gedruckt.

    – Der 19. Kämpfer

    3. Dezember 2021 um 21:33 Uhr


Zumindest in Visual C++: printf (und andere ACSII-Funktionen): %s stellt einen ASCII-String dar %S ist ein Unicode-String wprintf (und andere Unicode-Funktionen): %s ist ein Unicode-String %S ist ein ASCII-String

Soweit keine Compiler-Warnungen vorliegen, verwendet printf eine variable Argumentliste, wobei nur das erste Argument typgeprüft werden kann. Der Compiler ist nicht darauf ausgelegt, die Formatzeichenfolge zu analysieren und die übereinstimmenden Parameter zu überprüfen. Bei Funktionen wie printf ist das Sache des Programmierers

Benutzer-Avatar
71GA

Antwort A

Keine der obigen Antworten hat darauf hingewiesen, warum Sie einige Ihrer Abdrücke möglicherweise nicht sehen. Das liegt auch daran, dass es sich hier um Streams handelt (Das wusste ich nicht) und stream hat etwas namens Orientierung. Lassen Sie mich etwas von zitieren Dies Quelle:

Enge und breite Ausrichtung

Ein neu eröffneter Stream hat keine Orientierung. Der erste Aufruf einer I/O-Funktion legt die Orientierung fest.

Eine breite I/O-Funktion macht den Stream
breit orientiertmacht eine schmale I/O-Funktion den Stream eng orientiert. Einmal eingestellt, kann die Ausrichtung nur noch mit geändert werden offen.

Narrow I/O-Funktionen können nicht auf einem Wide-orientierten Stream aufgerufen werden; Wide I/O-Funktionen können nicht auf einem Narrow-orientierten Stream aufgerufen werden. Wide-I/O-Funktionen konvertieren zwischen Wide- und Multibyte-Zeichen wie beim Aufrufen mbrtowc und wcromb. Im Gegensatz zu den in einem Programm gültigen Multibyte-Zeichenfolgen dürfen Multibyte-Zeichenfolgen in der Datei eingebettete Nullen enthalten und müssen nicht im anfänglichen Umschaltzustand beginnen oder enden.

Also sobald Sie verwenden printf() Ihre Orientierung wird eng und Sie können von diesem Punkt an nichts mehr herausbekommen wprintf() und du wirklich nicht. Es sei denn, Sie verwenden freeopen() die für Dateien verwendet werden soll.


Antwort B

Wie sich herausstellt, können Sie verwenden freeopen() so was:

freopen(NULL, "w", stdout);             

Strom zu machen “nicht definiert” wieder. Versuchen Sie dieses Beispiel:

#include <stdio.h>
#include <wchar.h>
#include <locale.h>

int main(void)
{
    // We set locale which is the same as the enviromental variable "LANG=en_US.UTF-8".
    setlocale(LC_ALL, "en_US.UTF-8");

    // We define array of wide characters. We indicate this on both sides of equal sign
    // with "wchar_t" on the left and "L" on the right.
    wchar_t y[100] = L"🕽€ο Δικαιοπολις εν αγρω εστιν\n";

    // We print header in ASCII characters
    wprintf(L"content-type:text/html; charset:utf-8\n\n");

    // A newly opened stream has no orientation. The first call to any I/O function
    // establishes the orientation: a wide I/O function makes the stream wide-oriented,
    // a narrow I/O function makes the stream narrow-oriented. Once set, we must respect
    // this, so for the time being we are stuck with either printf() or wprintf().

    wprintf(L"%S\n", y);    // Conversion specifier %S is not standardized (!)
    wprintf(L"%ls\n", y);   // Conversion specifier %s with length modifier %l is 
                            // standardized (!)

    // At this point curent orientation of the stream is wide and this is why folowing
    // narrow function won't print anything! Whether we should use wprintf() or printf()
    // is primarily a question of how we want output to be encoded.

    printf("1\n");          // Print narrow string of characters with a narrow function
    printf("%s\n", "2");    // Print narrow string of characters with a narrow function
    printf("%ls\n",L"3");   // Print wide string of characters with a narrow function

    // Now we reset the stream to no orientation.
    freopen(NULL, "w", stdout);

    printf("4\n");          // Print narrow string of characters with a narrow function
    printf("%s\n", "5");    // Print narrow string of characters with a narrow function
    printf("%ls\n",L"6");   // Print wide string of characters with a narrow function

    return 0;
}

Benutzer-Avatar
Hypoano

Aus man fprintf

   C      (Not in C99 or C11, but in SUSv2, SUSv3, and SUSv4.)  Synonym for lc.  Don't use.

   S      (Not in C99 or C11, but in SUSv2, SUSv3, and SUSv4.)  Synonym for ls.  Don't use.

Verwenden Sie daher nicht %C oder %S, sondern immer %lc oder %ls.

%S scheint sich zu fügen Die Single Unix Specification v2 und ist auch Teil der aktuellen (2008) POSIX-Spezifikation.

Entsprechende C99-konforme Formatbezeichner wären %s und %ls.

  • Können Sie weitere Details angeben, z. B. einen Link?

    – Antonio

    17. Juli 2013 um 13:27 Uhr

  • 10 Sekunden an [search engine]: pubs.opengroup.org/onlinepubs/007908799/xsh/fprintf.html

    – David Förster

    17. Juli 2013 um 13:31 Uhr

  • Nun, in der Antwort steht “Single Unix Specification v2”, und Ihre Frage bezieht sich auf die “printf” -Familie. Da ist Ihre Suchanfrage.

    – David Förster

    17. Juli 2013 um 13:44 Uhr

  • Ich empfehle das Linken gegen POSIX 2008 (Open Group Issue 7, das der Single Unix Specification entspricht v4), anstelle des alten SUS V2. Die URL wäre: pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html

    – MestreLion

    12. März 2015 um 8:26 Uhr

  • @71GA: Aber ich kann es dort finden. Suchen Sie nach „Äquivalent zu ls“.

    – David Förster

    7. Februar 2020 um 12:37 Uhr

1377410cookie-checkprintf, wprintf, %s, %S, %ls, char* und wchar*: Fehler, die nicht durch eine Compiler-Warnung angekündigt werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy