函数参数从signed int转换为unsigned int

时间:2017-10-02 04:35:33

标签: c type-conversion implicit-conversion

在下面的代码片段中,我偶然发现了一个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线程

,期待详细解释也会很感激

2 个答案:

答案 0 :(得分:4)

看着你的代码,我想我明白你的困惑在哪里。您正在将-10作为unsigned值传递,并且您正在用“为什么打印-10如果它是”无符号值?

这一切都归结为比特。 10的位是(使用16位来防止大量的零)

0000000000001010

在大多数计算机中,负值以二进制补码格式存储(这是外行术语是所有位的反转加一)。如果应用两次,则返回相同的数字。因此,-10 twos-complement 存储空间为:

1111111111110110

这将很适合同一类型的unsigned值。

下一个窘境是“如何打印-10”只要您的值不超过int的最大值,"%d"就会愉快地将您提供的位打印为整数。您的代码将-10分配为int,然后传递为unsigned,因为您传递的值为1111111111110110,因此没有问题。您可以将值打印为signed int-10)或unsigned int4294967286)。

这是你回答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线程提供链接,因为没有它们我的答案是合理完成的。您可以自己搜索此类链接。