webentwicklung-frage-antwort-db.com.de

Was ist der Zweck der Modifizierer h und hh für printf

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?)

50
R..

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 .

13
Michael Burr

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.

5
caf

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.

5
Adam Norberg

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.

1

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).

1
mzimmers

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

https://github.com/Mellanox/libvma/commit/b5cb1e34a04b40427d195b14763e462a0a705d23#diff-6258d0a11a435aa373768037fe161d24

0
rafi wiener

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.

0
Jens Gustedt