函数参数从 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

在大多数计算机中,负值以二进制补码格式存储(外行术语是所有位加一的反转)。如果申请两次,您会得到相同的号码。因此,-10twos-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 值具有与值为 -10int 相同的按位表示。标准不保证这一点。

3) 正如我在上一点所说,由于类型不匹配,您对 printf() 的调用具有未定义的行为。将格式说明符更改为 %u 以获得明确定义的行为(然后您将看到打印的值将等于 UINT_MAX-9。将具有负值的 int 转换为 unsigned - 在 main() 中调用 set_data() 具有实现定义的行为时发生,因为 UINT_MAX 的值是实现定义的。换句话说,转换 set_data() 所产生的值=13=] 从 intunsigned 是实现定义的。

另请注意,就调用者而言,set_data() 的第二个参数上的 const 说明符是多余的,因为该参数是按值传递的。即使没有 const,函数对 data 所做的任何更改也只会在 set_data() 的主体内可见,而对调用者不可见。

我不会提供其他 SO 线程的链接,因为没有它们我的回答相当完整。您可以自己搜索此类链接。