C语言中的无符号整数数据类型

3
请帮我区分C语言中以下代码的不同之处:

代码1:

#include<stdio.h>
#include <stdint.h>

uint8_t fb(int a)
{
    return -3;
}

int main()
{
    int a = fb(-3);
    printf("%d",a);
    return 0;
}

代码 2:

#include<stdio.h>

unsigned int fb(int a)
{
    return -3;
}

int main()
{
    int a = fb(-3);
    printf("%d",a);
    return 0;
}

问题在于第一段代码按预期返回253,但是第二段代码返回了-3,这是不符合预期的,因为返回类型应该是无符号的。请问这是怎么可能的呢?
我使用的是Mingw GCC编译器。

答案是否假定“mingw gcc”是唯一的平台? - Deduplicator
4个回答

9
在第一个程序中,
  1. 当函数返回时,将类型为 int 的 -3(FFFFFFFD)强制转换为 uint8_t,结果是 253(FD)。高位字节被丢弃以适应。
  2. 当执行赋值操作时,将类型为 uint8_t 的 253(FD)强制转换为带符号的 int 类型,通过符号扩展为 253(000000FD)。
在第二个程序中,
  1. 当函数返回时,将类型为 int 的 -3(FFFFFFFD)强制转换为无符号的 unsigned int 类型,结果是 4294967293(FFFFFFFD)。不需要丢弃任何字节。
  2. 当执行赋值操作时,将类型为 unsigned int 的 4294967293(FFFFFFFD)强制转换为 int 类型,结果为 -3(FFFFFFFD)。
备注:
  • 假设使用32位整数,但64位整数也是类似的情况。
  • 这是对发生的情况的解释,不一定符合 C 规范。

3
我不明白您的意思。能否请您举更多例子来说明?谢谢! - user3798282
3
规则、例外、其他平台、解释或任何其他内容没有被提及吗? - Deduplicator
1
转换规则是不同的,但是如果做出正确的假设,包括适当地定义超出范围的转换为有符号数,这可能会发生。仍然没有提到规则或所有假设(OP从未说过他只想要x86 win-32位和/或win-64位)。 - Deduplicator

2
有符号整数转换为无符号整数的规则由draft C99 standard中的第6.3.1.3节“有符号和无符号整数”确定,该节表示:
否则,如果新类型是无符号的,则通过重复添加或减去一个比新类型中可以表示的最大值多1的值,直到该值在新类型的范围内为止进行转换。49) 这意味着将会通过加上UMAX+1来将-3转换。对于uint8_t,这是一个相对较小的值253,它适合于带符号整数,并因此没有问题地转换为带符号整数。
在你提到的第二种情况中,返回值是无符号整型unsigned int,在转换后我们会得到一个相当大的值,实际上是std::numeric_limits<unsigned int>::max() + 1 - 3。这个值将无法适应有符号整型signed int,这是溢出,因此根据第6.5段第5条所述,这是未定义行为:

如果在表达式求值期间发生异常情况(即结果在数学上没有定义或不在其类型可表示的范围内),则行为未定义。


非常感谢您:)愿上帝保佑您。 - user3798282
等待更多回复。 :) - user3798282

0

将整数类型转换为无符号整数类型的规则很简单:
根据需要添加/减去(目标类型的最大值+1)。

将整数类型转换为有符号整数类型的规则更简单:
保留值,如果超出范围,则转换未定义。
大多数现代实现(所有Windows平台上)定义为添加/减去与相应的unsigned类型相同的常量,直到适合为止。

使用这些规则,在第一个示例中从fb()返回的转换是保留值并且正确的:
-3 -> 256-3 = 253 -> 253

第二个示例中的返回值超出范围,因此具有未定义的行为,尽管通常情况下:
-3 -> 2CHAR_BIT*sizeof(int)-3 -> -3

额外的事实:

  • 将有可变参数的函数传递给需要相应另一种类型的已签名/无符号类型是允许的,尽管无法转换原始值,并且将被视为指定类型。
  • CHAR_BIT 是一个预处理器常量,给出了一个字节中的位数。
  • sizeof 是一种运算符,用于获取类型/变量的字节数。

无法理解2 **(CHAR_BIT * sizeof(int))语句?请帮忙,我是初学者。 - user3798282
我已经添加了你要求的那些常量的定义。 - Deduplicator
非常感谢您 :) 愿上帝保佑您。如果您能再解释一下,对我和其他人都会非常有帮助。 - user3798282

0

无符号整数应该使用%u而不是%d进行打印。


虽然没有“unsigned”变量需要打印。 - Deduplicator
在问题中指定了“返回类型为无符号”,代码中也是如此,但当你使用%d打印时,实际类型并不重要,它将打印一个有符号整数,因此要打印无符号值,你需要使用%u - in need of help
返回类型为无符号,但被赋值给了有符号变量。 - Deduplicator
@Deduplicator,你的回答非常好而且正确。然而,当你想要打印一个int时,无论变量是声明为int还是unsigned int都没有关系。系统只会分配相同的32位(或64位,取决于系统),然后当你请求打印时,它会查看%d%u并以那种方式显示这32位。 我的答案将修复所谓的“错误”,这个错误在于显示而不是变量本身的值。 - in need of help
这个“答案”只是一个评论,甚至没有尝试回答问题。 - ikegami
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接