我什么时候可以逃脱未通过签名声明int?

时间:2014-02-12 16:24:47

标签: c types printf undefined-behavior

在C中,像-1这样的有符号整数应该用关键字signed声明,如下所示:

signed int i = -1;

然而,我试过这个:

signed int i = -2;
unsigned int i = -2;
int i = -2;

并且所有3个案例都打印出-2 printf("%d", i);。为什么呢?

5 个答案:

答案 0 :(得分:3)

打印整数变量的方式受到传递给printf的格式字符串的影响:

  • 如果您使用%d,那么您将打印为有符号整数。
  • 如果您使用%u,那么您将打印为无符号整数。

答案 1 :(得分:3)

由于您确认使用以下方式进行打印:

printf("%d", i);

这是未签名案例中的undefined behaviordraft C99 standard部分7.19.6.1 fprintf函数中介绍了格式说明符的printf,它在段落 9 中说明:

  

如果转换规范无效,则行为未定义。 248) [...]

3.4.3 部分未定义的行为中定义的标准为:

  

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

并进一步说明:

  

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

最后,我们可以看到 int signed int 相同。我们可以通过转到6.7.2 类型说明符部分看到这一点,在段落 2 中将 int 分组如下:

  

int,signed或signed int

以及稍后在段落 5 中说:

  

除了bit-field [...]

之外,每个逗号分隔的集都指定相同的类型

答案 2 :(得分:1)

printf无法知道你传递给它的是什么。 C编译器在传递参数时执行默认类型提升,然后函数本身根据您传递的格式说明符重新解释值,因为它没有关于值的类型的其他信息你通过了。

当您将unsigned int传递给位居printf的{​​{1}}时,它是未定义的行为。你的程序不正确,它可以打印任何东西。

在两个补码表示中代表负数的硬件上会出现与您开始时相同的数字。但是,这不是一个普遍规则。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

你有两件事情在继续:

  1. 签名和无符号是解释相同64位(或32位或其他)位的不同方式。
  2. Printf是一种可变函数,它接受不同类型的参数
  3. 您将有符号值(-2)传递给无符号变量,然后请求printf将其解释为已签名。

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

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

    在大多数架构中,加法和减法的工作方式相同(只要你没有使用一些不使用2的补码表示负数的古怪架构) 乘法和除法也可以相同。 不平等比较是最难以了解它们将如何工作的事情,而且我已经多次对有符号和无符号进行比较,我认为这样做是正常的,因为它们处于小的有符号数范围内。

    多数是“未定义”的含义。行为由编译器和硬件实现者完成,并且在架构之间甚至在同一架构上都不能相同。