【问题标题】:Conversion of function parameter from signed int to unsigned int将函数参数从有符号整数转换为无符号整数
【发布时间】:2017-10-02 04:35:32
【问题描述】:

在我前几天偶然发现的以下代码 sn-p 中,它将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 线程,也将不胜感激

【问题讨论】:

  • 好吧,因为int 将始终适合unsigned,(2)您告诉printfa 中的位解释为int(看看@ 987654332@ 整数存储以获取更多详细信息)。代码定义明确。
  • #2 printf("Inside set_data:%d\n", data);data > INT_MAX 时是 UB。
  • @chux - printf() 在格式说明符指定与相应参数不同的类型时具有未定义的行为。
  • @peter C11 §6.5.2.2 6 和 §7.21.6.1 9 存在一定程度的不一致,可以解释为 int/unsigned 值的表示方式与 printf() 相同,与 "%u/%d" 无法区分.在这种情况下,OP 的 unsigned 值肯定是 > INT_MAX 等等 UB。

标签: c type-conversion implicit-conversion


【解决方案1】:

看了你的代码,我想我明白你的困惑在哪里。您将-10 作为unsigned 值传递,而您却在挠头,“如果它是一个`无符号值,为什么还要打印-10

这一切都归结为一点点。 10 的位是(使用16 位来防止出现很多零)

0000000000001010

在大多数计算机中,负值以 twos-complement 格式存储(外行术语是所有位加一的反转)。如果申请两次,您将获得相同的号码。所以,-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。否则,你很好。

消化一下,如果我理解您的担忧,或者如果我错过了标记,请告诉我,我很乐意提供进一步的帮助。

【讨论】:

  • 是的,这是我最关心的问题。感谢您的深入分析!
【解决方案2】:

1) 编译器没有“大声疾呼”(更正确的描述是“发出诊断”)关于隐式转换,因为从 signed 类型到 unsigned 类型的转换由标准明确定义 - 它使用什么是(数学上)称为模算术。 set_data(&a, -10) 的调用在调用set_data() 之前对-10 进行了这样的转换。值-10 转换为unsigned 时将产生UINT_MAX - 9 值,其中UINT_MAX 是从<limits.h> 获得的,表示unsigned int 可以表示的最大值。编译器通常可以配置为提供有关此类转换的警告,但默认情况下,大多数编译器都配置为不提供。如果您需要警告,则需要阅读编译器的文档以了解如何操作。

2) 您对printf() 的所有调用都会导致未定义的行为,因为%d 通知printf() 相应的参数是int 类型,并且您已经传递了unsigned(即类型不匹配) )。在您的特定实现中,int 类型的值与unsigned 之间的转换似乎不会更改按位表示,并且传递给printf()unsigned 值具有与int 相同的按位表示值为-10。标准不保证这一点。

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

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

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

【讨论】:

  • 感谢您的解释 - 关于数据的 const 说明符,我更喜欢向 ppl 稍后阅读/处理此功能传达一条消息 - 此参数不要被弄乱 - 应该写照原样..
  • 按值传递已经传达了这个信息——就调用者而言,值不会改变。按值传递函数参数是 C 中的一个基本概念。
猜你喜欢
  • 2012-01-09
  • 2017-07-21
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 2011-06-14
相关资源
最近更新 更多