Jaki jest cel modyfikatorów h I hh Dla printf?

Oprócz %hn i %hhn (Gdzie h lub hh określa rozmiarwskazywanego na obiektu), jaki jest punkt modyfikatorów h i hh dla specyfikacji formatu printf?

Ze względu na domyślne promocje, które są wymagane przez standard do zastosowania dla funkcji wariacyjnych, nie jest możliwe przekazanie argumentów typu char lub short (lub dowolnych podpisanych/niepodpisanych ich wariantów) do printf.

Zgodnie z 7.19.6.1(7), modyfikator h:

Określa, że następujący specyfikator konwersji d, I, o, U, x lub X ma zastosowanie do short int lub unsigned short int argument (argument będzie były promowane zgodnie z promocjami integer, ale ich wartość powinna przed wydrukowaniem należy przekonwertować na krótką liczbę całkowitą lub niepodpisaną krótką liczbę całkowitą); lub że następujący N specyfikator konwersji stosuje się do wskaźnika do krótkiej int argument.

Jeśli argument był rzeczywiście typu short lub unsigned short, to awans do int, po którym następuje konwersja z powrotem do short lub unsigned short da taką samą wartość jak promocja do int bez konwersji z powrotem. Zatem dla argumentów typu short lub unsigned short, %d, %u, itd. powinno dać identyczne wyniki do %hd, %hu, itd. (i podobnie dla typów char i hh).

Z tego, co wiem, jedyną sytuacją, w której modyfikator h lub hh może być użyteczny, jest sytuacja, w której argument przekazał mu int poza zakresem short lub unsigned short, np.

printf("%hu", 0x10000);

Ale rozumiem, że podanie niewłaściwego typu powoduje i tak nieokreślone zachowanie, więc nie można oczekiwać, że wyświetli 0.

Jeden prawdziwy przypadek, który widziałem, to kod taki:

char c = 0xf0;
printf("%hhx", c);

Gdzie autor oczekuje, że wydrukuje f0 pomimo, że implementacja ma zwykły typ char, który jest podpisany (w takim przypadku printf("%x", c) wydrukuje fffffff0 lub podobny). Ale czy to oczekiwanie jest uzasadnione?

(Uwaga: chodzi o to, że oryginalny typ to char, który jest promowany do int i konwertowany z powrotem do unsigned char zamiast char, zmieniając w ten sposób wartość, która zostanie wydrukowana. Ale czy standard określa to zachowanie, czy jest to szczegół implementacji, na którym może polegać zepsute oprogramowanie?)

Author: hippietrail, 2011-01-03

7 answers

Jeden możliwy powód: dla symetrii z użyciem tych modyfikatorów w sformatowanych funkcjach wejściowych? Wiem, że to nie byłoby absolutnie konieczne, ale może dostrzegano w tym jakąś wartość?

Chociaż nie wspominają o znaczeniu symetrii dla modyfikatorów "h" I "hh" w w dokumencie uzasadnienia C99, Komitet wspomina o tym jako o tym, dlaczego specyfikator konwersji "%p " jest wspierany dla fscanf() (nawet jeśli nie było to nowe dla C99 - wsparcie "%p" jest w C99). C90): {]}

Konwersja wskaźnika wejściowego z %p została dodana do C89, chociaż jest to oczywiście ryzykowne, dla symetrii z fprintf.

W sekcji dotyczącej fprintf() Dokument C99 mówi o tym, że dodano "hh" , a jedynie odsyła czytelnika do sekcji fscanf():

Modyfikatory długości %hh I %ll zostały dodane w C99(zob. §7.19.6.2).

Wiem, że to wątek wątkowy, ale i tak spekuluję, więc pomyślałem, że podam jakikolwiek argument możliwe.

Również, dla kompletności, modyfikator "h" był w oryginalnym standardzie C89 - prawdopodobnie byłby tam nawet, gdyby nie był ściśle konieczny ze względu na powszechne stosowanie, nawet jeśli nie byłoby wymogu technicznego użycia modyfikatora.

 13
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 18:36:00

W trybie %...x wszystkie wartości są interpretowane jako niepodpisane. Liczby ujemne są zatem drukowane jako ich niepodpisane konwersje. W arytmetyce dopełniacza 2, używanej przez większość procesorów, nie ma różnicy we wzorcach bitowych między podpisaną liczbą ujemną a jej dodatnim, niepodpisanym odpowiednikiem, który jest zdefiniowany przez arytmetykę modułową (dodając maksymalną wartość dla pola plus jeden do liczby ujemnej, zgodnie ze standardem C99). Dużo oprogramowania - zwłaszcza kod debugowania najbardziej prawdopodobnie użycie %x - zakłada ciche założenie, że bitowa reprezentacja podpisanej wartości ujemnej i jej niepodpisanego rzutu jest taka sama, co jest prawdziwe tylko na maszynie dopełniacza 2.

Mechanika tego odlewu jest taka, że szesnastkowe reprezentacje wartości zawsze sugerują, być może niedokładnie, że liczba została wyrenderowana w dopełnieniu 2, o ile nie osiągnęła warunku krawędzi, gdzie różne reprezentacje liczb całkowitych mają różne zakresy. To nawet trzyma true dla reprezentacji arytmetycznych, gdzie wartość 0 nie jest reprezentowana wzorem binarnym wszystkich 0s.

Negatyw short wyświetlany jako unsigned long w hexidecimal będzie zatem na każdej maszynie wyściełany f, ze względu na niejawne rozszerzenie znaku w promocji, które printf wydrukuje. Wartość jest taka sama, ale jest wizualnie myląca co do wielkości pola, co oznacza znaczną ilość zakresu, który po prostu nie jest obecny.

%hx truncates wyświetlana reprezentacja, aby uniknąć tego wypełnienia, dokładnie tak, jak wywnioskowałeś z rzeczywistego przypadku użycia.

Zachowanie printf jest niezdefiniowane, gdy przekazywana jest int poza zakresem short, który powinien być wydrukowany jako short, ale najprostsza implementacja zdecydowanie odrzuca wysoki bit przez surowy downcast, więc podczas gdy specyfikacja nie wymaga żadnego konkretnego zachowania, prawie każda rozsądna implementacja po prostu wykona obcięcie. Generalnie są lepsze ale sposoby na to.

Jeśli printf nie jest wypełnianiem wartości lub wyświetla niepodpisane reprezentacje podpisanych wartości, %h nie jest zbyt użyteczne.

 5
Author: Adam Norberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 19:01:58

Jedyne zastosowanie, jakie przychodzi mi do głowy, to podanie unsigned short LUB unsigned char i użycie specyfikacji konwersji %x. Nie możesz po prostu użyć nagiego %x - wartość może być promowana do int, a nie unsigned int, a wtedy masz nieokreślone zachowanie.

Alternatywą jest albo jawne oddanie argumentu do unsigned; albo użycie %hx / %hhx z gołym argumentem.

 5
Author: caf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-04 00:23:12

Zmienne argumenty printf() et al są automatycznie promowane przy użyciu domyślnych konwersji, więc dowolne short lub char wartości są promowane do int po przekazaniu do funkcji.

W przypadku braku modyfikatorów h lub hh, należy zamaskować przekazane wartości, aby uzyskać prawidłowe zachowanie. Dzięki modyfikatorom nie trzeba już maskować wartości; implementacja printf() wykonuje zadanie prawidłowo.

W szczególności dla formatu %hx, kod wewnątrz printf() może zrobić coś takiego:

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`!

Zakładam, że short jest liczbą 16-bitową; standard tego oczywiście nie gwarantuje.

 1
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 20:27:39

Uznałem, że przydatne jest unikanie rzucania podczas formatowania znaków niepodpisanych na hex:

        sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));

Jest to niewielka wygoda kodowania i wygląda czystiej niż wiele odlewów (IMO).

 1
Author: mzimmers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 17:49:10

Zgadzam się z Tobą, że nie jest to bezwzględnie konieczne, a więc tylko z tego powodu nie jest dobre w funkcji biblioteki C:)

Może to być" miłe "dla symetrii różnych flag, ale jest to w większości przeciwne do produktywności, ponieważ ukrywa regułę" konwersja do int".

 0
Author: Jens Gustedt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 23:13:06

Innym przydatnym miejscem jest sprawdzanie rozmiaru snprintf. gcc7 dodano sprawdzanie rozmiaru podczas korzystania z snprintf więc to się nie uda

char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);

Więc wymusza użycie większego znaku podczas używania %d podczas formatowania znaku

Oto commit, który pokazuje te poprawki zamiast zwiększania rozmiaru tablicy znaków, które zmienili %d na %h. to również daje dokładniejsze Opis

Https://github.com/Mellanox/libvma/commit/b5cb1e34a04b40427d195b14763e462a0a705d23#diff-6258d0a11a435aa372068037fe161d24

 0
Author: rafi wiener,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-01 09:57:59