什么时候 uint8_t ≠ unsigned char?

Posted

技术标签:

【中文标题】什么时候 uint8_t ≠ unsigned char?【英文标题】:When is uint8_t ≠ unsigned char? 【发布时间】:2013-04-14 19:56:57 【问题描述】:

根据 C 和 C++,CHAR_BIT >= 8. 但无论何时CHAR_BIT > 8uint8_t 都不能表示为 8 位。 它必须更大,因为CHAR_BIT 是系统上任何数据类型的最小位数。

在什么样的系统上,uint8_t 可以合法地定义为unsigned char 以外的类型?

(如果 C 和 C++ 的答案不同,那么我想两者都知道。)

【问题讨论】:

@Mysticial:不,我认为所有chars 都必须让他们所有的表示位参与确定他们的价值。 或者可能是 16 位 uint8_t,其中 8 是实数,8 是填充。不过,我会拍谁创造了这样的环境。 :) C++ 标准将其列为可选。 typedef signed integer type int8_t; // optional @Mysticial:标准要求 [u]int*_t 没有填充位,并且如果有符号则为二进制补码。 @Mysticial:这样的环境确实存在(对于 DSP 架构来说,无法解决任何小于一个单词的问题是很常见的);在这种情况下,uint8_t 根本不应该存在。 【参考方案1】:

如果存在,uint8_t 必须始终与unsigned char 具有相同的宽度。但是,它不必是同一类型;它可能是一个不同的扩展整数类型。它也不必具有与unsigned char 相同的表示;例如,可以以相反的顺序解释这些位。这是一个愚蠢的例子,但它对int8_t 更有意义,其中signed char 可能是一个补码或符号幅度,而int8_t 需要是二进制补码。

即使在“正常”系统上,为uint8_t 使用非字符扩展整数类型的另一个“优势”是 C 的别名规则。字符类型可以给任何东西起别名,这可以防止编译器对同时使用字符指针和指向其他类型的指针的函数进行大量优化,除非restrict 关键字应用得很好。然而,即使uint8_t 具有与unsigned char 完全相同的大小和表示形式,如果实现使其成为不同的非字符类型,则别名规则将不适用于它,并且编译器可以假定类型的对象例如,uint8_tint 永远不能使用别名。

【讨论】:

typedef __uint8_t uint8_t; 是一个类型定义。 为了幽默,也许实现可能会决定与其命名约定保持一致,并且与long long 相比,它可能会引入short short。因此,typedef short short int8_t;... 在 2003 ± 2 年(现在不打算在邮件档案中挖掘它),GCC 团队考虑制作 [u]int8_t 特殊的扩展整数类型,以便可以更积极地对其进行优化。 .. 但最终拒绝了这个概念,理由是程序员很可能期望它们具有与char 相同的特殊别名属性。 (大约在同一时间,我们因进行基于类型的别名分析而被内核人员尖叫根本,所以我们都有点紧张。) @Zack:感谢您提供有趣的历史记录。如果 gcc 仍然提供这些类型,但默认情况下不使用它们,那就太好了,以便功能测试宏或类似的可以切换到它们,从而实现更积极的优化。 从字符类型中分离 uint8_t 实际上在 GCC bugzilla 中讨论过:参见 gcc.gnu.org/bugzilla/show_bug.cgi?id=66110>。【参考方案2】:

在什么样的系统上uint8_t可以合法地定义为unsigned char以外的类型?

总而言之,uint8_t 只能在CHAR_BIT 为 8 的系统上合法定义。它是一个可寻址单元,只有 8 个值位且没有填充位。

具体来说,CHAR_BIT 定义了最小可寻址单元的宽度,uint8_t 不能有填充位;只有当最小的可寻址单元正好是 8 位宽时,它才能存在。如果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 类型的操作数时,无符号 char,或signed char,(或其合格版本)结果是 1. ...

因此,这些是(一些)最小的可寻址单元。显然int8_tuint8_t 也可以被认为是最小的可寻址单元,前提是它们存在。

7.20.1.1 精确宽度整数类型

1 typedef 名称 intN_t 指定有符号整数类型,宽度 N、无填充位和二进制补码表示。因此, int8_t 表示这样一个宽度正好为 8 的有符号整数类型 位。

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

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

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

【讨论】:

如果uint8_tunsigned char 没有什么不同,那么它的目的是什么? @Mehrdad 我猜在你真正需要int8 的情况下,当CHAR_BIT > 8 时它根本不会编译,因为int8_t 甚至不存在。而如果使用 charCHAR_BIT > 8,那么你可能会得到一个半损坏的构建。 不同于unsigned charunsigned char 保证存在,但仅在CHAR_BIT == 8 时保证为 8 位。 uint8_t 不保证存在,但保证为 8 位。 charint8_t 之间存在细微差别,除了宽度。 char 可能使用一个补码、二进制补码或符号和大小表示,其中 int8_t 需要使用二进制补码表示。 我一直认为所有特定尺寸类型的意义在于,如果发生奇怪的事情,事情要么继续工作,要么立即崩溃并告诉你。当您不使用 chars 时,它们的可读性也更高。【参考方案3】:

迄今为止没有人提到的一种可能性:如果CHAR_BIT==8 和不合格的char 是无符号的,它在某些ABI 中,那么uint8_t 可能是char 的typedef,而不是unsigned char。这至少在它影响重载选择(及其邪恶的孪生兄弟,名称修改)的范围内很重要,即如果您要在范围内同时拥有foo(char)foo(unsigned char),则使用uint8_t 类型的参数调用foo 会在这样的系统上更喜欢foo(char)

【讨论】:

"但是,它不必是相同的类型;它可以是不同的扩展整数类型。"部分涵盖了这一点,尽管它确实很容易被忽视。 @LucDanton char 不是扩展整数类型。 “它不必是同一类型”是相关部分。我以其余的为例。

以上是关于什么时候 uint8_t ≠ unsigned char?的主要内容,如果未能解决你的问题,请参考以下文章

错误:从“uint8_t* aka unsigned char*”转换为“unsigned int”会丢失精度 [-fpermissive]

uint8_t 和 unsigned char 链接错误

uint8_t uint32_t 类型强制转换出错 以及 unsigned char 类型和 unsigned int 类型相互转化

React Native Build Error on IOS - typedef用不同类型重新定义('uint8_t'(又名'unsigned char')与'enum clockid_t')

freeRTOS与裸机程序相比有什么区别??

react-native 无法构建 ios:(错误 xcode Flipper)Typedef 重新定义不同类型('uint8_t'(又名'unsigned char')与'enum clockid_