函数参数从 signed int 到 unsigned int 的转换
Conversion of function parameter from signed int to unsigned int
在我前几天偶然发现的以下代码片段中,它将 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. 为什么 "Inside set_data" 打印负整数,而 data
明确定义为 unsigned int
3.这是否是偶然的未定义行为或定义的实现,这可能不是编写安全可移植代码的好习惯?
期待详细的解释,如果您能指出相关的 SO 线程,我们将不胜感激
看了你的代码,我想我明白你的困惑在哪里了。您将 -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
。这就是你告诉 printf
和 %u
或 %d
.
未定义行为没有问题,只要您不尝试使用 a
中大于 INT_MAX
的值作为 int
。否则,你很好。
消化一下,让我知道我是否理解你的担忧,或者如果我没有达到目标,我很乐意进一步提供帮助。
1) 编译器不"shouting"(更正确的描述是"issuing a diagnostic")关于隐式转换,因为从signed
类型到unsigned
类型的转换是标准明确定义 - 它使用(数学上)称为模算术的东西。 set_data(&a, -10)
的调用在调用 set_data()
之前执行了 -10
的这种转换。将 -10
的值转换为 unsigned
将得到 UINT_MAX - 9
的值,其中 UINT_MAX
是从 <limits.h>
获得的,表示最大值 a unsigned int
可以表示。编译器通常可以配置为发出有关此类转换的警告,但默认情况下,大多数编译器都没有配置为。如果你想要警告,你需要阅读你的编译器的文档来弄清楚如何。
2) 你对 printf()
的所有调用都会导致未定义的行为,因为 %d
通知 printf()
相应的参数是 int
类型,并且你有传递了 unsigned
(即类型不匹配)。对于您的特定实现,int
类型的值与 unsigned
之间的转换似乎不会更改按位表示,并且传递给 printf()
的 unsigned
值具有与值为 -10
的 int
相同的按位表示。标准不保证这一点。
3) 正如我在上一点所说,由于类型不匹配,您对 printf()
的调用具有未定义的行为。将格式说明符更改为 %u
以获得明确定义的行为(然后您将看到打印的值将等于 UINT_MAX-9
。将具有负值的 int
转换为 unsigned
- 在 main()
中调用 set_data()
具有实现定义的行为时发生,因为 UINT_MAX
的值是实现定义的。换句话说,转换 set_data()
所产生的值=13=] 从 int
到 unsigned
是实现定义的。
另请注意,就调用者而言,set_data()
的第二个参数上的 const
说明符是多余的,因为该参数是按值传递的。即使没有 const
,函数对 data
所做的任何更改也只会在 set_data()
的主体内可见,而对调用者不可见。
我不会提供其他 SO 线程的链接,因为没有它们我的回答相当完整。您可以自己搜索此类链接。
在我前几天偶然发现的以下代码片段中,它将 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. 为什么 "Inside set_data" 打印负整数,而 data
明确定义为 unsigned int
3.这是否是偶然的未定义行为或定义的实现,这可能不是编写安全可移植代码的好习惯?
期待详细的解释,如果您能指出相关的 SO 线程,我们将不胜感激
看了你的代码,我想我明白你的困惑在哪里了。您将 -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
。这就是你告诉 printf
和 %u
或 %d
.
未定义行为没有问题,只要您不尝试使用 a
中大于 INT_MAX
的值作为 int
。否则,你很好。
消化一下,让我知道我是否理解你的担忧,或者如果我没有达到目标,我很乐意进一步提供帮助。
1) 编译器不"shouting"(更正确的描述是"issuing a diagnostic")关于隐式转换,因为从signed
类型到unsigned
类型的转换是标准明确定义 - 它使用(数学上)称为模算术的东西。 set_data(&a, -10)
的调用在调用 set_data()
之前执行了 -10
的这种转换。将 -10
的值转换为 unsigned
将得到 UINT_MAX - 9
的值,其中 UINT_MAX
是从 <limits.h>
获得的,表示最大值 a unsigned int
可以表示。编译器通常可以配置为发出有关此类转换的警告,但默认情况下,大多数编译器都没有配置为。如果你想要警告,你需要阅读你的编译器的文档来弄清楚如何。
2) 你对 printf()
的所有调用都会导致未定义的行为,因为 %d
通知 printf()
相应的参数是 int
类型,并且你有传递了 unsigned
(即类型不匹配)。对于您的特定实现,int
类型的值与 unsigned
之间的转换似乎不会更改按位表示,并且传递给 printf()
的 unsigned
值具有与值为 -10
的 int
相同的按位表示。标准不保证这一点。
3) 正如我在上一点所说,由于类型不匹配,您对 printf()
的调用具有未定义的行为。将格式说明符更改为 %u
以获得明确定义的行为(然后您将看到打印的值将等于 UINT_MAX-9
。将具有负值的 int
转换为 unsigned
- 在 main()
中调用 set_data()
具有实现定义的行为时发生,因为 UINT_MAX
的值是实现定义的。换句话说,转换 set_data()
所产生的值=13=] 从 int
到 unsigned
是实现定义的。
另请注意,就调用者而言,set_data()
的第二个参数上的 const
说明符是多余的,因为该参数是按值传递的。即使没有 const
,函数对 data
所做的任何更改也只会在 set_data()
的主体内可见,而对调用者不可见。
我不会提供其他 SO 线程的链接,因为没有它们我的回答相当完整。您可以自己搜索此类链接。