Abgesehen von %hn
und %hhn
(wobei h
oder hh
die Größe des point-to -Objekts angibt), wo liegen die Modifizierer h
und hh
für printf
-Formatbezeichner?
Aufgrund von Standard-Promotions, die vom Standard für variadische Funktionen verwendet werden müssen, ist es nicht möglich, Argumente des Typs char
oder short
(oder aller signierten/unsignierten Varianten davon) an printf
zu übergeben.
Gemäß 7.19.6.1 (7) wird der Modifizierer h
Gibt an, dass eine folgende Konvertierungsspezifikation für d, i, o, u, x oder X für eine .__ gilt. short int oder unsigned short int Argument (das Argument wird entsprechend den Integer-Promotions befördert, aber sein Wert soll vor dem Druck in short int oder unsigned short int konvertiert werden); oder dass eine folgende n-Konvertierungsspezifikation für einen Zeiger auf eine kurze .__ gilt. int Argument.
Wenn das Argument tatsächlich vom Typ short
oder unsigned short
war, führt die Beförderung zu int
, gefolgt von einer Konvertierung zurück zu short
oder unsigned short
, zu demselben value als Beförderung zu int
, ohne dass eine Konvertierung erfolgt. Daher sollten für Argumente vom Typ short
oder unsigned short
, %d
, %u
usw. identische Ergebnisse zu %hd
, %hu
usw. (und ebenfalls für char
-Typen und hh
) erzielt werden.
Soweit ich das beurteilen kann, ist die einzige Situation, in der der Modifizierer h
oder hh
möglicherweise nützlich sein könnte, wenn das Argument eine int
außerhalb des Bereichs von short
oder unsigned short
übergeben hat, z.
printf("%hu", 0x10000);
ich verstehe jedoch, dass das Weitergeben des falschen Typs ohnehin zu undefiniertem Verhalten führt, sodass Sie nicht erwarten konnten, dass 0 ausgegeben wird.
Ein realer Fall, den ich gesehen habe, ist folgender Code:
char c = 0xf0;
printf("%hhx", c);
wenn der Autor erwartet, dass f0
gedruckt wird, obwohl die Implementierung einen einfachen char
-Typ hat, der signiert ist (in diesem Fall würde printf("%x", c)
fffffff0
oder ähnliches drucken). Aber ist diese Erwartung gerechtfertigt?
(Hinweis: Der ursprüngliche Typ war char
, der zu int
befördert und wieder in unsigned char
und nicht in char
konvertiert wird, wodurch der Wert, der gedruckt wird, geändert wird. Aber der Standard gibt dieses Verhalten an oder ist ein Implementierungsdetail dass defekte Software darauf angewiesen ist?)
Ein möglicher Grund: für die Symmetrie bei der Verwendung dieser Modifikatoren in den formatierten Eingangsfunktionen? Ich weiß, dass es nicht unbedingt notwendig wäre, aber vielleicht wurde dafür Wert gesehen?
Obwohl sie nicht die Bedeutung der Symmetrie für die Modifizierer "h" und "hh" in dem C99 Rationale-Dokument erwähnen, erwähnt das Komitee dies als eine Überlegung, warum der Konvertierungs-Spezifizierer "% p" unterstützt wird fscanf()
(auch wenn das für C99 nicht neu war - "% p" wird in C90 unterstützt):
Die Eingabezeigerumwandlung mit% p wurde zu C89 hinzugefügt, obwohl es offensichtlich für die Symmetrie mit fprintf riskant ist.
In dem Abschnitt zu fprintf()
wird im C99-Begründungsdokument erwähnt, dass "hh" hinzugefügt wurde, der Leser wird jedoch lediglich auf den Abschnitt fscanf()
verwiesen:
Die Längenmodifikatoren% hh und% ll wurden in C99 hinzugefügt (siehe §7.19.6.2).
Ich weiß, dass es ein zäher Faden ist, aber ich spekuliere sowieso, also dachte ich, ich würde alles geben, was es gibt.
Der Vollständigkeit halber befand sich der "h" -Modifikator im ursprünglichen C89-Standard. Vermutlich wäre er auch dort vorhanden, wenn dies aufgrund der weit verbreiteten Verwendung nicht unbedingt erforderlich war, selbst wenn möglicherweise keine technische Anforderung für die Verwendung des Modifizierers bestand .
Die einzige Verwendung, die ich mir vorstellen kann, ist die Übergabe eines unsigned short
oder unsigned char
und die Verwendung des %x
-Konvertierungsspezifizierers. Sie können nicht einfach einen bloßen %x
verwenden - der Wert kann zu int
anstelle von unsigned int
heraufgestuft werden, und dann haben Sie undefiniertes Verhalten.
Ihre Alternativen bestehen entweder darin, das Argument explizit in unsigned
umzuwandeln. oder %hx
/%hhx
mit einem bloßen Argument verwenden.
Im %...x
-Modus werden alle Werte als vorzeichenlos interpretiert. Negative Zahlen werden daher als nicht signierte Konvertierungen gedruckt. In der Zweierkomplement-Arithmetik, die die meisten Prozessoren verwenden, gibt es keinen Unterschied in den Bitmustern zwischen einer vorzeichenbehafteten negativen Zahl und ihrem positiven vorzeichenlosen Äquivalent, die durch die Modulus-Arithmetik definiert wird (Addition des maximalen Werts für das Feld plus der negativen Zahl entsprechend) zum C99-Standard). Viele Software - insbesondere der Debugging-Code, der am wahrscheinlichsten %x
verwendet - setzt die stillschweigende Annahme voraus, dass die Bitdarstellung eines negativen Vorzeichens mit Vorzeichen und dessen vorzeichenlose Umwandlung die gleiche ist, was nur auf einem 2er-Komplement-Computer zutrifft.
Die Mechanik dieser Besetzung ist so, dass hexadezimale Wertedarstellungen immer, möglicherweise ungenau, bedeuten, dass eine Zahl als Zweierkomplement dargestellt wurde, solange sie keine Randbedingung erfüllt, bei der die verschiedenen Ganzzahldarstellungen unterschiedliche Bereiche haben. Dies gilt sogar für arithmetische Darstellungen, bei denen der Wert 0 nicht mit dem binären Muster aller 0 dargestellt wird.
Eine negative short
, die in hexidecimal als unsigned long
angezeigt wird, wird daher auf jedem Computer mit f
aufgefüllt, da die Aktion das implizite Vorzeichen in der Aktion enthält, die printf
druckt. Der Wert ist derselbe, aber es ist wirklich visuell irreführend hinsichtlich der Größe des Feldes, was eine beträchtliche Menge an Reichweite impliziert, die einfach nicht vorhanden ist.
%hx
schneidet die angezeigte Darstellung ab, um diese Auffüllung zu vermeiden, genau wie Sie es in Ihrem realen Anwendungsfall festgestellt haben.
Das Verhalten von printf
ist undefiniert, wenn eine int
außerhalb des Bereichs von short
übergeben wird, die als short
ausgegeben werden soll, aber die einfachste Implementierung löscht einfach das High-Bit durch einen rohen Downcast, sodass die Spezifikation nicht erfordert jedes spezifische Verhalten, so ziemlich jede vernünftige Implementierung wird nur die Kürzung durchführen. Es gibt im Allgemeinen bessere Möglichkeiten, dies zu tun.
Wenn printf keine Werte auffüllt oder nicht signierte Repräsentationen von signierten Werten anzeigt, ist %h
nicht sehr nützlich.
Die variadischen Argumente für printf()
et al werden automatisch mit den Standardkonvertierungen heraufgestuft, sodass alle short
- und char
-Werte bei der Übergabe an die Funktion auf int
heraufgestuft werden.
Wenn keine Modifizierer h
oder hh
vorhanden ist, müssen Sie die übergebenen Werte maskieren, um das korrekte Verhalten zuverlässig zu erhalten. Mit den Modifikatoren müssen Sie die Werte nicht mehr maskieren. Die Implementierung von printf()
erledigt den Job ordnungsgemäß.
Für das Format %hx
kann der Code in printf()
Folgendes tun:
va_list args;
va_start(args, format);
...
int i = va_arg(args, int);
unsigned short s = (unsigned short)i;
...print s correctly, as 4 hex digits maximum
...even on a machine with 64-bit `int`!
Ich gehe mutwillig davon aus, dass short
eine 16-Bit-Menge ist; Der Standard garantiert das natürlich nicht.
Ich fand es nützlich, das Casting zu vermeiden, wenn Sie unsignierte Zeichen in Hex formatieren:
sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));
Es ist ein kleiner Codierkomfort und sieht sauberer aus als mehrere Casts (IMO).
ein weiterer Ort, an dem es praktisch ist, ist die Überprüfung der Größe von Snprintf . Bei der Verwendung von Snprintf gcc7 wurde die Größe überprüft
char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);
sie müssen also bei der Formatierung eines Zeichens ein größeres Zeichen verwenden, wenn Sie% d verwenden
hier ist ein Commit, der diese Korrekturen anzeigt, statt die Größe des Zeichen-Arrays zu erhöhen, die sie% d in% h geändert haben. Dies gibt auch eine genauere Beschreibung
Ich stimme mit Ihnen überein, dass dies nicht unbedingt notwendig ist, und aus diesem Grund allein ist es in einer C-Bibliotheksfunktion nicht gut :)
Es könnte "Nizza" für die Symmetrie der verschiedenen Flags sein, ist aber meistens kontraproduktiv, da die Regel "Konvertierung in int
" verborgen wird.