在下面的代码片段中,我偶然发现了一个signed int
传递给期望unsigned int
#include "stdio.h"
void set_data(unsigned int* addr, const unsigned int data)
{
printf("Inside set_data:%d\n", data);
*addr = data;
}
int main() {
unsigned int a = 2;
printf("Before set_data:%d\n", a);
set_data(&a, -10);
printf("After set_data:%d\n", a);
return 0;
}
输出: -
请注意,编译器不会抛出任何警告: -
zhossain@zhossain-linux1:~$ gcc -Wall test.c
zhossain@zhossain-linux1:~$ ./a.out
Before set_data:2
Inside set_data:-10
After set_data:-10
的问题: -
1.为什么编译器不喊?隐式转换?
2.怎么来"里面的set_data"打印一个负整数,而data
明确定义为unsigned int
3.这是否有任何机会定义未定义的行为或实现,这可能不是编写安全可移植代码的好习惯?
如果你能指出相关的SO线程
,期待详细解释也会很感激答案 0 :(得分:4)
看着你的代码,我想我明白你的困惑在哪里。您正在将-10
作为unsigned
值传递,并且您正在用“为什么打印-10
如果它是”无符号值?“
这一切都归结为比特。 10
的位是(使用16
位来防止大量的零)
0000000000001010
在大多数计算机中,负值以二进制补码格式存储(这是外行术语是所有位的反转加一)。如果应用两次,则返回相同的数字。因此,-10
的 twos-complement 存储空间为:
1111111111110110
这将很适合同一类型的unsigned
值。
下一个窘境是“如何打印-10
?”只要您的值不超过int
的最大值,"%d"
就会愉快地将您提供的位打印为整数。您的代码将-10
分配为int
,然后传递为unsigned
,因为您传递的值为1111111111110110
,因此没有问题。您可以将值打印为signed int
(-10
)或unsigned int
(4294967286
)。
这是你回答printf
的唯一问题。它应该用这些位做什么?“我打算11111111111111111111111111110110
作为unsigned
打印还是打印成int
。这就是你用{{1}告诉printf
的内容}或%u
。
未定义行为没有问题,只要您不尝试将%d
中的值大于a
作为INT_MAX
。否则,你很好。
消化这个,如果我理解你的担忧,或者如果我错过了这个标记,我很乐意帮助我。
答案 1 :(得分:2)
1)编译器没有“大喊”(更正确的描述是“发布诊断”)关于隐式转换,因为从signed
类型到unsigned
类型的转换由标准 - 它使用(数学上)称为模运算的东西。在调用set_data(&a, -10)
之前,-10
的调用会对set_data()
进行此类转换。转换为-10
时,unsigned
的值将导致值为UINT_MAX - 9
,其中UINT_MAX
从<limits.h>
获得并代表最大值a {{1}可以代表。编译器通常可以配置为发出有关此类转换的警告,但默认情况下,大多数编译器都配置为不。如果您需要警告,则需要阅读编译器的文档以了解如何使用。
2)您对unsigned int
的所有调用都会导致未定义的行为,因为printf()
会通知%d
相应的参数类型为printf()
,并且您已通过int
(即类型不匹配)。对于您的特定实现,似乎将unsigned
类型的值转换为int
或从unsigned
转换不会改变按位表示,并且传递给unsigned
的{{1}}值具有具有值printf()
的{{1}}的相同按位表示。标准不保证这一点。
3)正如我在上一点所说,由于类型不匹配,您对int
的调用具有未定义的行为。将格式说明符更改为-10
以获得明确定义的行为(然后您将看到打印的值将等于printf()
。将%u
转换为负值{ {1}} - 当UINT_MAX-9
中的int
调用具有实现定义的行为时会发生这种情况,因为unsigned
的值是实现定义的。换句话说,转换生成的值{从set_data()
到main()
的{1}}是实现定义的。
另请注意,就调用者而言,UINT_MAX
的第二个参数上的-10
说明符是多余的,因为该参数是按值传递的。即使没有int
,函数对unsigned
所做的任何更改也只会在const
的正文中显示,并且对调用者不可见。
我不会给其他SO线程提供链接,因为没有它们我的答案是合理完成的。您可以自己搜索此类链接。