Hexadecimal representation of a common data type value in C
I was trying to write a function in C to print the hex representation of the generic value of a data type. My first attempt failed, my second succeeded, why?
-
First try
#include <stdio.h> /* prints out the hexadecimal representation of a generic value */ void show_hex(void*,size_t); int main() { int i = 12345; short s = 32767; /* 2^15 - 1 */ show_hex(&i, sizeof(int)); show_hex(&s, sizeof(short)); return 0; } void show_hex(void *x, size_t sz){ char *cx = x; int i; printf("0x"); for (i = 0; i < sz; ++i) printf("%.2x", cx[i]); printf("\n"); }
Conclusion:
0x39300000
For a caseint
as expected on a small target machine (Mac OSX). For case,short
it outputs0xffffffff7f
instead of0xff7f
. -
Second try:
typedef unsigned char *byte_pointer; int main() { int i = 12345; short s = 32767; /* 2^15 - 1 */ show_hex((byte_pointer)&i, sizeof(int)); show_hex((byte_pointer)&s, sizeof(short)); return 0; } void show_hex(byte_pointer x, size_t sz){ int i; printf("0x"); for (i = 0; i < sz; ++i) printf("%.2x", x[i]); printf("\n"); }
This program outputs as expected for both cases,
int: 0x39300000
andshort: 0xff7f
In the first case, change:
printf("%.2x", cx[i]);
to
printf("%.2x", cx[i] & 0xFF);
The normal type is char
signed, so 0xFF is signed to the 8-byte value of all Fs, as shown in the printout.
The first number worked fine as all bytes were in the range 0x00..0x7F, so the value was not converted to a negative integer — it remained positive. The second number failed because one of the bytes was in the range 0x80..0xFF and therefore was converted to a negative integer. And %.2x
will always print all digits, but if there is only one, there will be a 0 in front of that digit. x
expects unsigned int
.
With printf()
and other variadic functions, arguments in ...
the argument list portion are encouraged by default, so types char
(all flavors) are promoted to int
(as is short
) and float
- double
.
This is described in the C standard (ISO / IEC 9899-2011):
6.5.2.2 Calling functions
6 If the expression that denotes the function being called is of a type that does not include a prototype, whole promotions are executed for each argument and arguments that are of type
float
, up todouble
. They are called the default argument Stocks ....7 If the expression that denotes the called function is of a type that includes a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, assuming the type of each parameter is an unqualified version of its declared type. The ellipsis notation in a function prototype declaration causes an argument type conversion to stop after the last declared parameter. Default argument promotions are executed on trailing arguments.
8 No other conversions are performed implicitly; in particular, the number and types of arguments are not compared to parameters in the function definition, which is not included in the function prototype declarator.
The half-bend is tangential.
The rest of paragraph 6 includes:
If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (
, ...
) or ..., the behavior is undefined.
This sentence should be read in the context of "If the expression that denotes the called function is of a type that does not include a prototype, ..." at the beginning of the paragraph. This means that if you call a function defined using ellipsis , ...
, but there is no prototype in scope at the time of the call, the behavior is undefined. You need to make sure you have a prototype in scope when you call the variadic function.
source to share