什么情况下uint8_t不等于unsigned char?

67
根据 C 和 C++ 标准,CHAR_BIT >= 8。 但是当 CHAR_BIT > 8 时,uint8_t 甚至不能表示为 8 位。 它必须更大,因为 CHAR_BIT 是系统上任何数据类型的最小位数。
在什么样的系统上可以合法地将 uint8_t 定义为与 unsigned char 不同的类型? (如果 C 和 C++ 的答案不同,那么我想知道两者的答案。)

1
@Mysticial:不,我认为所有的char都必须让它们的表示位参与确定它们的值。 - user541686
2
或者是一个16位的uint8_t,其中8位是真实的,另外8位是填充的。不过如果有人制造出这样的环境,我会开枪打他的 :) - Mysticial
9
C++标准将其列为可选项。typedef signed integer type int8_t; // optional - Rapptz
8
根据标准,int*_t类型必须没有填充位,并且如果有符号的话必须采用二进制补码表示法。 - R.. GitHub STOP HELPING ICE
1
@Mysticial:这样的环境确实存在(DSP架构通常无法寻址比一个字更小的任何内容);在这种情况下,uint8_t根本不应该存在。 - Mike Seymour
显示剩余4条评论
3个回答

65
如果存在,uint8_t必须始终与unsigned char具有相同的宽度。但是,它不需要是相同的类型;它可以是一个独立的扩展整数类型。它也不需要与unsigned char具有相同的表示;例如,位可以按相反的顺序解释。这是一个愚蠢的例子,但对于int8_t来说更有意义,其中signed char可能是补码或符号大小格式,而int8_t则要求为二进制补码。
在“正常”系统上使用非char扩展整数类型作为uint8_t的另一个“优点”是C的别名规则。字符类型允许别名任何东西,这会防止编译器大量优化同时使用字符指针和指向其他类型的指针的函数,除非已经应用了restrict关键字。但是,即使uint8_t具有与unsigned char完全相同的大小和表示方式,如果实现将其作为独立的非字符类型,则别名规则也不适用于它,编译器可以假设类型为uint8_tint的对象永远不可能成为别名。

12
typedef __uint8_t uint8_t; 是一个类型定义。 - R.. GitHub STOP HELPING ICE
3
为了幽默起见,也许一个实现可能会决定与它的命名约定保持一致,与“long long”相反,引入一个“short short”。因此,可以使用“typedef short short int8_t;”进行定义。 - autistic
26
在大约 2003 年左右(现在不想去查邮件档案了),GCC 团队曾考虑将 [u]int8_t 设为特殊的扩展整数类型,以便更积极地进行优化......但最终出于程序员非常可能期望它们具有与 char 相同的特殊别名属性而拒绝了这个想法。(当时我们正因为进行基于类型的别名分析而被内核开发人员痛斥,所以我们都有点胆怯。) - zwol
3
@Zack:谢谢你提供这个有趣的历史注释。如果gcc仍然提供这些类型但不默认使用它们,那将很好,这样一个功能测试宏或类似的东西就可以切换到它们,启用更激进的优化。 - R.. GitHub STOP HELPING ICE
3
在GCC的Bugzilla上实际上讨论了将uint8_t与字符类型分离的问题,请参见https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66110。 - user3840170
显示剩余4条评论

33

uint8_t只能在CHAR_BIT为8的系统上合法定义为除了unsigned char之外的类型。它是一个可寻址单元,恰好有8个值位和没有填充位。

具体地说,CHAR_BIT定义了最小可寻址单元的宽度,而uint8_t不能有填充位;只有当最小可寻址单元恰好为8位宽时才能存在uint8_t。如果提供了CHAR_BIT等于8,则可以通过类型定义将uint8_t定义为任何没有填充位的8位无符号整数类型。


C11标准草案(n1570.pdf)中如下规定:

5.2.4.2.1 整数类型的大小 1 下面给出的值应替换为适用于#if预处理指令的常量表达式。 ……它们的实现定义值必须与所示的值相等或者比其绝对值大(同样的符号)。

-- number of bits for smallest object that is not a bit-field (byte)
   CHAR_BIT                                            8
因此,最小的对象必须恰好包含CHAR_BIT位。
  

6.5.3.4 sizeof和_Alignof运算符

     

...

     

4当sizeof应用于具有char、unsigned char或signed char类型(或其限定版本)的操作数时,结果为1. ...

因此,它们是(一些)最小的可寻址单元。显然,如果存在,则也可以将int8_t和uint8_t视为最小的可寻址单元。
  

7.20.1.1 精确宽度整型

     

1typedef名称intN_t指定了带有宽度N、没有填充位和二进制补码表示的有符号整数类型。因此,int8_t表示具有恰好8位宽度的这种有符号整数类型。

     

2typedef名称uintN_t指定了一个无填充位且宽度为N的无符号整数类型。因此,uint24_t表示具有恰好24位宽度的此类无符号整数类型。

     

3这些类型是可选的。但是,如果实现提供具有8、16、32或64位宽度、没有填充位并且(对于有符号类型)具有二进制补码表示的整数类型,则必须定义相应的typedef名称。

强调“这些类型是可选的”。希望这有所帮助 :)

4
在需要使用 int8 的情况下,如果 CHAR_BIT > 8,则 int8_t 不存在,代码根本无法编译通过。而如果使用 char,并且 CHAR_BIT > 8,那么可能会得到一个半破损的版本。请注意,这里不是解释性的内容。 - Mysticial
7
unsigned charuint8_t 是不同的。unsigned char 确保存在,但只有在 CHAR_BIT == 8 时才保证为8位。当 uint8_t 存在时,它保证是8位,但不能保证它一定存在。 - autistic
15
除了宽度之外,charint8_t 之间存在微妙的区别。char 可能使用补码、反码或原码表示,而 int8_t 必须使用二进制补码表示。 - autistic
6
我一直认为所有特定大小的类型存在的意义是,如果发生了奇怪的事情,它们要么会继续工作,要么会立即停止并告诉你故障原因。此外,当你不使用 char 时,它们更容易阅读。 - ssube
@autistic char 可以是 unsigned,而 int8_t 是有符号的。 - 12431234123412341234123
显示剩余7条评论

8
迄今为止没有人提到的一种可能性是:如果CHAR_BIT==8并且未经限定的char是无符号的,这在某些ABIs中是这样的,那么uint8_t可以是char的typedef,而不是unsigned char。至少在影响重载选择(及其恶魔孪生兄弟,名称编码)方面很重要,即如果您同时拥有作用域内的foo(char)foo(unsigned char),则使用类型为uint8_t的参数调用foo将优先选择foo(char)

1
然而,它不一定是相同类型;它可能是一个不同的扩展整数类型。尽管它确实可能被忽视。 - Luc Danton
2
@LucDanton char 不是扩展整数类型。 - zwol
2
“它不需要是相同的类型”是相关部分。我认为其余部分是一个例子。 - Luc Danton

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