我什么时候可以逃脱未通过签名声明int?
在C中,应该使用关键字signed
来声明像-1这样的有符号整数,如下所示:
signed int i = -1;
但是,我试过这个:
signed int i = -2; unsigned int i = -2; int i = -2;
所有3个案例用printf("%d", i);
打印出-2 printf("%d", i);
。 为什么?
由于您确认使用以下方式打印:
printf("%d", i);
这是未签名情况下的未定义行为 。 这在C99标准草案第7.19.6.1
节草案中 7.19.6.1
介绍7.19.6.1
函数也包含了格式说明符的printf
,它在第9段中说明:
如果转换规范无效,则行为未定义。 248) […]
3.4.3
节中定义的标准未定义行为如下:
行为,在使用不可移植或错误的程序结构或错误数据时,本国际标准不对此要求
并进一步说明:
可能的未定义行为包括完全忽略不完整结果的情况,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的特定文档执行,终止翻译或执行(发布时)一条诊断信息)。
最后,我们可以看到int与signed int相同。 我们可以通过转到6.7.2
类型说明符来看到这一点,在第2段中它将int分组如下:
int,signed或signed int
后来在第5段中说:
每个逗号分隔的集合指定相同的类型,除了位字段[…]
打印整数变量的方式受到传递给printf
的格式字符串的影响:
- 如果您使用
%d
,那么您将打印为有符号整数。 - 如果您使用
%u
,那么您将打印为无符号整数。
printf
无法知道你传递给它的是什么。 C编译器在传递参数时执行默认类型提升,然后函数本身会根据您传递的格式说明符重新解释值,因为它没有关于您传递的值类型的其他信息。
将unsigned int
传递给%d
位置的printf
时,它是未定义的行为。 你的程序不正确,它可以打印任何东西。
碰巧的是,在二进制补码表示中代表负数的硬件上,您获得与您开始时相同的数字。 但是,这不是一个普遍的规则。
unsigned int i = -2; // i actually holds 4294967294 printf("%d", i); // printf casts i back to an int which is -2 hence the same output
你有两件事情要做:
- 有符号和无符号是解释相同的64(或32或其他)位的不同方式。
- Printf是一种可变函数,它接受不同类型的参数
您将带符号的值(-2)传递给无符号变量,然后请求printf将其解释为已签名。
请记住,“签名”和“无符号”与如何对数字进行算术运算有关。 printf系列接受内部转换根据格式指示符传入的任何内容。 (这是可变函数的性质,它接受多种类型的参数。它们不能使用传统的类型安全机制)
这一切都很好,但并非所有事情都会起作用。
在大多数体系结构中,加法和减法都是一样的(只要你没有使用一些不使用2的补码表示负数的奇数结构,乘法和除法也可以使用相同的。不等式比较是最难理解的他们会工作,我有点多次在签名和未签名之间进行比较,我认为这样可以,因为他们在小签名数字范围内。
这就是“未定义”的含义。 行为留给编译器和硬件实现者,并且不能在架构之间或甚至在同一架构上随时间变得相同。