我什么时候可以逃脱未通过签名声明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节中定义的标准未定义行为如下:

行为,在使用不可移植或错误的程序结构或错误数据时,本国际标准不对此要求

并进一步说明:

可能的未定义行为包括完全忽略不完整结果的情况,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的特定文档执行,终止翻译或执行(发布时)一条诊断信息)。

最后,我们可以看到intsigned 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 

你有两件事情要做:

  1. 有符号和无符号是解释相同的64(或32或其他)位的不同方式。
  2. Printf是一种可变函数,它接受不同类型的参数

您将带符号的值(-2)传递给无符号变量,然后请求printf将其解释为已签名。

请记住,“签名”和“无符号”与如何对数字进行算术运算有关。 printf系列接受内部转换根据格式指示符传入的任何内容。 (这是可变函数的性质,它接受多种类型的参数。它们不能使用传统的类型安全机制)

这一切都很好,但并非所有事情都会起作用。

在大多数体系结构中,加法和减法都是一样的(只要你没有使用一些不使用2的补码表示负数的奇数结构,乘法和除法也可以使用相同的。不等式比较是最难理解的他们会工作,我有点多次在签名和未签名之间进行比较,我认为这样可以,因为他们在小签名数字范围内。

这就是“未定义”的含义。 行为留给编译器和硬件实现者,并且不能在架构之间或甚至在同一架构上随时间变得相同。