2015-02-07 9 views
7

folgenden angenommen wird:printf Format für 1-Byte-Zahl mit Vorzeichen

sizeof(char) = 1 
sizeof(short) = 2 
sizeof(int) = 4 
sizeof(long) = 8 

Das printf Format für eine 2-Byte-Zahl mit Vorzeichen ist %hd, für eine 4-Byte-Zahl mit Vorzeichen ist %d, für eine 8-Byte-Zahl mit Vorzeichen sind %ld , aber was ist das richtige Format für eine 1-Byte-Zahl mit Vorzeichen?

+0

FYI: 'sizeof (char)' ist in der Spezifikation definiert, immer '1' zu sein, unabhängig von der tatsächlichen Größe von' char'. –

+0

Gibt es etwas falsches, es zu einem 'int' zu promoten und nur'% d' zu benutzen? – 5gon12eder

+2

Was Ihre Frage betrifft, möchten Sie vielleicht lesen, z. [Diese Referenz] (http://en.cppreference.com/w/c/io/fprintf). –

Antwort

6

Was ist das richtige Format für eine 1 Byte vorzeichenbehaftete Zahl? .

%hh und die ganze Zahl Konvertierungsspezifizierer Ihrer Wahl (zum Beispiel %02hhX Siehe die C11-Standard, § 7.21.6.1p5:

hh

Gibt an, dass eine folgende d, i , o, u, x, oder X Konvertierungsspezifizierer gilt für ein vorzeichenbehaftetes char oder unsigned char Argument (das Argument wird wurden gemäß den Integer-Promotions hochgestuft, aber ihr Wert sollte vor dem Drucken in ein Zeichen mit oder ohne Vorzeichen umgewandelt werden; & hellip;

Der Kommentar in Klammern ist wichtig. Wegen Integer-Promotions für die Argumente für Variadic-Funktionen (z. B. printf) wird der Funktion nie ein char-Argument angezeigt. Viele Programmierer denken, dass dies bedeutet, dass es unnötig ist, h und hh Qualifier zu verwenden. Sicher, Sie erstellen kein undefiniertes Verhalten, indem Sie sie auslassen, und die meiste Zeit wird es funktionieren.

Allerdings kann char gut signiert werden, und die Ganzzahlpromotion behält seinen Wert bei, was es zu einer vorzeichenbehafteten Ganzzahl macht. Wenn Sie die vorzeichenbehaftete Ganzzahl mit einem unsignierten Format (z. B. %02X) ausgeben, erhalten Sie die Vorzeichenerweiterung F s. Wenn Sie also signierte char mit einem unsignierten Format anzeigen möchten, müssen Sie printf mitteilen, wie groß die ursprüngliche Breite des Integer-Typs ohne Promodierung war, indem Sie hh verwenden.

Im Fall, dass nicht klar ist, ein einfaches Beispiel (aber umstritten) Beispiel war:

/* Read the comments thread to this post; I'll remove 
    this note when I edit the outcome of the discussion into 
    the answer 
*/ 

#include <stdio.h> 
int main(void) { 
    char* s = "\u00d1"; /* Ñ */ 
    for (char* p = s; *p; ++p) printf("%02X (%02hhX)\n", *p, *p); 
    return 0; 
} 

Ausgang:

$ ./a.out 
FFFFFFC3 (C3) 
FFFFFF91 (91) 

Im Kommentar-Thread gibt es (oder möglicherweise war) beträchtliche Diskussion darüber, ob das obige Snippet undefiniertes Verhalten ist, weil die Formatspezifikation X ein vorzeichenloses Argument erfordert, während das char Argument i s (zumindest auf der Implementierung, die die vorgestellte Ausgabe produziert) signiert. Ich denke, dieses Argument beruht auf § 7.12.6.1/p9: "Wenn ein Argument nicht der richtige Typ für die entsprechende Konvertierungsspezifikation ist, ist das Verhalten nicht definiert."

jedoch im Fall von char (und short) integer-Typen, die die Expression in der Argumentliste auf int oder unsigned int gefördert, bevor die Funktion aufgerufen wird.(Es ist erwähnenswert, dass auf den meisten Architekturen, alle drei Zeichentypen werden zu einem unterzeichnet int gefördert werden, die Förderung eines unsigned char (oder ohne Vorzeichen char) zu einem unsigned int nur auf einer Implementierung geschehen wird, wo sizeof(int) == 1.)

So auf Bei den meisten Architekturen wird das Argument einer %hx oder %hhx Formatkonvertierung signiert, und das kann kein undefiniertes Verhalten sein, ohne die Verwendung dieser Formatcodes bedeutungslos zu machen.

Darüber hinaus sagt der Standard nicht, dass fprintf (und Freunde) irgendwie den ursprünglichen Ausdruck wiederherstellen. Was es sagt ist, dass der Wert "konvertiert werden soll in unterzeichnet Char oder unsigned char vor dem Drucken" (§ 7.21.6.1/p5, oben zitiert, Hervorhebung hinzugefügt).

Die Konvertierung eines vorzeichenbehafteten Werts in einen vorzeichenlosen Wert ist nicht undefiniert. Es ist nicht einmal unspezifiziert oder implementierungsabhängig. Es besteht einfach darin, (konzeptuell) "einen Wert mehr als den Maximalwert zu addieren oder zu subtrahieren, der im neuen Typ dargestellt werden kann, bis der Wert im Bereich des neuen Typs liegt." (§ 6.3.1.3/p2)

So gibt es ein gut definiertes Verfahren das Argument Ausdruck auf ein (möglicherweise signiert) int Argument und ein gut definiertes Verfahren zur Umwandlung für diesen Wert zu einem unsigned char umwandelt. Ich behaupte daher, dass ein Programm wie das oben dargestellte völlig klar definiert ist.

Für corroboration wird das Verhalten von fprintf ein Format-Spezifizierer angegeben %c definiert als (§ 7.21.6.8/p8) folgt, Hervorhebungen hinzugefügt:

die int Argument auf ein unsigned char umgewandelt und das resultierende Zeichen wird geschrieben.

Wenn man die vorgeschlagene restriktive Auslegung anzuwenden waren, die das obige Programm nicht definiert macht, dann glaube ich, dass man auch argumentieren, dass gezwungen sein würde, zu:

void f(char c) { 
    printf("This is a '%c'.\n", c); 
} 

war auch UB. Aber ich denke, fast jeder C-Programmierer hat etwas Ähnliches geschrieben, ohne darüber nachzudenken.

Der entscheidende Teil der Frage ist, was mit "Argument" in §7.12.6.1/p9 (und anderen Teilen von §7.12.6.1) gemeint ist. Der C++ - Standard ist etwas genauer; es gibt an, dass, wenn ein Argument den Standardargument-Promotions unterliegt, "der Wert des Arguments vor dem Aufruf in den hochgestuften Typ konvertiert wird", was ich unter Berücksichtigung des Aufrufs interpretiere (zum Beispiel den Aufruf von fprintf), Die Argumente sind jetzt die geförderten Werte.

Ich glaube nicht, dass C tatsächlich anders ist, zumindest in der Absicht. Es verwendet Formulierungen wie "die Argumente & Hellips; werden gefördert", und an mindestens einem Ort "das Argument nach der Promotion". Darüber hinaus wird in der Beschreibung der Variadic-Funktionen (va_arg Makro, § 7.16.1.1) die Einschränkung für den Argumenttyp in Aneinanderreihung "der Typ des tatsächlichen nächsten Arguments (wie nach den Standard-Argument-Promotions gefördert wird)".

Ich stimme völlig zu, dass all dies (a) subtile Lektüre von ungenügend präziser Sprache ist und (b) tanzende Engel zählt. Aber ich sehe keinen Wert in der Erklärung, dass Standardverwendungen wie die Verwendung von %c mit char Argumente "technisch" UB sind; das den Begriff von UB denaturiert und es schwer zu glauben ist, dass ein solches Verbot absichtlich wäre, was mich zu der Annahme verleitet, dass die Interpretation nicht beabsichtigt war. (Und vielleicht sollte redaktionell korrigiert werden.)

+0

Was ist der Zweck des" aber sein Wert soll vor dem Ausdruck in ein vorzeichenbehaftetes Zeichen oder ein unsigniertes Zeichen umgewandelt werden "? Was bewirkt diese zusätzliche Konvertierung? (Siehe auch meinen Kommentar an Joachim unter der Frage.) –

+1

@MikeNakis: Ich habe die Frage bearbeitet, um das zu beantworten, in der Erwartung, dass jemand es wissen möchte. Lassen Sie mich wissen, ob die Änderung hilft. – rici

+0

Ja, es hilft, danke! (Und ja, natürlich, warum habe ich nicht daran gedacht!) –