我试图用 C 编写一个函数来打印出通用数据类型值的十六进制表示。我的第一次尝试失败了,第二次成功了,为什么?
第一次尝试
#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");
}
输出:0x39300000
对于 int
情况,如小端计算机 (Mac OSX) 上预期的那样。对于 short
情况,它输出 0xffffffff7f
而不是 0xff7f
。
第二次尝试:
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");
}
对于这两种情况,此程序都按预期输出,int: 0x39300000
和
短:0xff7f
对于第一种情况,改变:
printf("%.2x", cx[i]);
到
printf("%.2x", cx[i] & 0xFF);
普通的 char
类型是有符号的,因此 0xFF 被符号扩展为全 F 的 8 字节值,如打印输出所示。
第一个数字工作正常,因为所有字节都在 0x00..0x7F 范围内,因此该值没有转换为负整数——它保持正数。第二个数字没有计算出来,因为其中一个字节在 0x80..0xFF 范围内,因此被转换为负整数。 %.2x
将始终打印所有数字,但如果只有一个,则该数字前会有一个 0。 x
需要一个 unsigned int
。
对于 printf()
和其他可变参数函数,参数列表的 ...
部分中的参数默认提升,因此 char
类型(所有品种)被提升为 int
(short
),float
被提升为 double
.
这记录在 C 标准 (ISO/IEC 9899-2011) 中:
6.5.2.2 Function calls
6 If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float
are promoted to double
. These are called the default argument
promotions. …
7 If the expression that denotes the called function has a type that does include a prototype,
the arguments are implicitly converted, as if by assignment, to the types of the
corresponding parameters, taking the type of each parameter to be the unqualified version
of its declared type. The ellipsis notation in a function prototype declarator causes
argument type conversion to stop after the last declared parameter. The default argument
promotions are performed on trailing arguments.
8 No other conversions are performed implicitly; in particular, the number and types of
arguments are not compared with those of the parameters in a function definition that
does not include a function prototype declarator.
半切向一边。
第 6 段的其余部分包括:
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.
这句话必须在“如果表示被调用函数的表达式的类型不包含
原型(prototype),......'在段落的开头。这意味着,如果您调用一个用省略号 , ...
定义的函数,但调用时范围内没有原型(prototype),则行为未定义。每当调用可变参数函数时,都必须确保范围内有原型(prototype)。