uint8_t 与无符号字符
Posted
技术标签:
【中文标题】uint8_t 与无符号字符【英文标题】:uint8_t vs unsigned char 【发布时间】:2010-12-16 02:24:28 【问题描述】:在 C 中使用 uint8_t
与 unsigned char
相比有什么优势?
我知道在几乎每个系统上uint8_t
只是unsigned char
的类型定义,
那为什么要使用它呢?
【问题讨论】:
【参考方案1】:它记录了您的意图 - 您将存储小数字,而不是一个字符。
如果您使用其他类型定义,例如 uint16_t
或 int32_t
,它看起来会更好。
【讨论】:
显式使用unsigned char
或 signed char
也记录了意图,因为朴素的 char
表明您正在使用字符。
我认为朴素的unsigned
是unsigned int
的定义?
@endolith,对字符串使用 uint8_t 不一定是错的,但肯定很奇怪。
@endolith,我想我可以用 UTF8 文本为 uint8_t 提供一个案例。实际上,char
似乎暗示了一个字符,而在 UTF8 字符串的上下文中,它可能只是多字节字符的一个字节。使用 uint8_t 可以清楚地表明,不应期望每个位置都有一个字符——换句话说,字符串/数组的每个元素都是一个任意整数,不应对其进行任何语义假设。当然,所有 C 程序员都知道这一点,但它可能会促使初学者提出正确的问题。
我不得不说,unsigned char
并不是真正用来存储字符的,所以“意图”问题没有实际意义。【参考方案2】:
正如你所说,“几乎每个系统”。
char
可能是不太可能改变的一种,但一旦你开始使用uint16_t
和朋友,使用uint8_t
会更好地融合,甚至可能成为编码标准的一部分。
【讨论】:
【参考方案3】:在我遇到的几乎所有系统上 uint8_t == unsigned char,但 C 标准不能保证这一点。如果您正在尝试编写可移植代码并且内存大小很重要,请使用 uint8_t。否则使用无符号字符。
【讨论】:
uint8_t
always 匹配unsigned char
的范围和大小以及unsigned char
为8 位时的填充(无)。当unsigned char
不是8位时,uint8_t
不存在。
@chux,您是否参考了标准中的确切位置?如果unsigned char
是8 位的,那么uint8_t
是否保证是其中的typedef
而不是扩展无符号整数类型 的typedef
?
@hsivonen “它在标准中的确切位置是什么?” --> 否 - 还需要查看 7.20.1.1。很容易推断,unsigned char/signed char/char
是最小的类型——不小于 8 位。 unsigned char
没有填充。对于uint8_t
,它必须是8 位,没有填充,因为实现提供的整数类型而存在:匹配unsigned char
的最低要求。至于“...保证是 typedef ...”看起来是个不错的问题。【参考方案4】:
为了迂腐,有些系统可能没有 8 位类型。根据Wikipedia:
当且仅当它具有满足要求的任何类型时,需要一个实现来定义 N = 8、16、32 或 64 的精确宽度整数类型。不需要为任何其他 N 定义它们,即使它支持适当的类型。
所以uint8_t
不能保证存在,尽管它适用于 8 位 = 1 字节的所有平台。一些嵌入式平台可能会有所不同,但这种情况非常罕见。某些系统可能将 char
类型定义为 16 位,在这种情况下,可能不会有任何类型的 8 位类型。
除了那个(次要)问题,@Mark Ransom's answer 在我看来是最好的。使用最清楚地表明您将数据用于什么目的的那个。
另外,我假设您的意思是 uint8_t
(stdint.h
标头中提供的 C99 标准 typedef)而不是 uint_8
(不是任何标准的一部分)。
【讨论】:
@caf,纯粹出于好奇——你能链接到一些描述吗?我知道它们的存在是因为有人在 comp.lang.c++.moderated 讨论中提到了一个(并链接到它的开发人员文档),讨论 C/C++ 类型保证是否太弱,但我再也找不到那个线程了,而且它总是很方便在任何类似的讨论中引用它:) “某些系统可能将 char 类型定义为 16 位,在这种情况下可能不会有任何类型的 8 位类型。” - 尽管我提出了一些不正确的反对意见,Pavel 在他的回答中已经证明,如果 char 是 16 位,那么即使编译器确实提供了 8 位类型,它不得调用它uint8_t
(或 typedef 到那个)。这是因为 8bit 类型在存储表示中会有未使用的位,uint8_t
一定没有。
SHARC 架构有 32 位字。详情请见en.wikipedia.org/wiki/…。
TI 的 C5000 DSP(在 OMAP1 和 OMAP2 中)是 16 位的。我认为对于 OMAP3,他们选择了 C6000 系列,具有 8 位字符。
深入研究 N3242 -“工作草案,C++ 编程语言标准”,第 18.4.1 节 typedef unsigned integer type uint8_t; // optional
因此,本质上,不需要符合 C++ 标准的库完全定义 uint8_t(参见注释 //可选)很少。从可移植性的角度来看,char
不能小于 8 位,也不能小于 char
,因此如果给定的 C 实现具有无符号 8 位整数类型,则它将是 char
。或者,它可能根本没有,此时任何typedef
技巧都没有实际意义。
它可以用来更好地记录您的代码,因为很明显您需要 8 位字节,而无需其他任何东西。但在实践中,它几乎在任何地方都是一个合理的期望(在某些 DSP 平台上它不是真的,但是你的代码在那里运行的机会很小,你也可以在你的程序顶部使用静态断言出错这样的平台)。
【讨论】:
为了记录,您可以在任何平台上制作一个 8 位类型:typedef struct unsigned i :8; uint8_t;
但您必须将其用作uint8_t x; x.i = ...
,这样会比较麻烦。
@Skizz - 不,标准要求 unsigned char
能够保存 0 到 255 之间的值。如果你能在 4 位中做到这一点,我的帽子向你致敬。
“会更麻烦一些” - 从某种意义上说,您必须步行(游泳、乘飞机等)一直走到编译器编写者所在的位置,这很麻烦,拍他们的后脑勺,让他们在实现中添加uint8_t
。我想知道,具有 16 位字符的 DSP 的编译器通常会实现uint8_t
,还是不实现?
顺便说一下,再想一想,这可能是说“我真的需要 8 位”-#include <stdint.h>
并使用uint8_t
最直接的方式。如果平台有,它会给你。如果平台没有,你的程序将无法编译,原因就很清楚了。
Still no cigar, sorry: "对于 unsigned char 以外的无符号整数类型,对象表示的位应分为两组:值位和填充位...如果有 N 个值位,每个位应表示 1 和 2^(N-1) 之间的 2 的不同幂,因此该类型的对象应能够使用纯二进制表示表示从 0 到 2^(N-1) 的值。 .. typedef 名称 intN_t 指定宽度为 N、无填充位和二进制补码表示的有符号整数类型。”【参考方案6】:
重点是编写独立于实现的代码。 unsigned char
不保证是 8 位类型。 uint8_t
是(如果有的话)。
【讨论】:
...如果它存在于系统上,但这将非常罕见。 +1 好吧,如果你真的因为 uint8_t 不存在而无法在系统上编译代码时遇到问题,你可以使用 find 和 sed 自动将所有出现的 uint8_t 更改为 unsigned char 或更有用的东西你。 @bazz - 如果您假设它是 8 位类型,则不能 - 例如,解压缩远程系统以字节方式打包的数据。隐含的假设是 uint8_t 不存在的原因是在 char 超过 8 位的处理器上。 抛出断言 assert(sizeof(unsigned char) == 8); @bazz 不正确的断言恐怕。sizeof(unsigned char)
将返回 1
1 个字节。但如果系统 char 和 int 的大小相同,例如 16 位,则 sizeof(int)
也将返回 1
【参考方案7】:
这非常重要,例如在您编写网络分析器时。 包头是由协议规范定义的,而不是特定平台的 C 编译器的工作方式。
【讨论】:
当我问这个问题时,我定义了一个简单的串行通信协议。【参考方案8】:根据我的经验,我们希望在两个地方使用 uint8_t 来表示 8 位(以及 uint16_t 等),并且我们可以使用小于 8 位的字段。这两个地方都很重要,我们在调试时经常需要查看数据的原始转储,并且需要能够快速确定它代表什么。
首先是在射频协议中,尤其是在窄带系统中。在这种环境下,我们可能需要将尽可能多的信息打包到一条消息中。第二个是闪存存储,我们的空间可能非常有限(例如在嵌入式系统中)。 在这两种情况下,我们都可以使用打包数据结构,编译器会为我们处理打包和解包:
#pragma pack(1)
typedef struct
uint8_t flag1:1;
uint8_t flag2:1;
padding1 reserved:6; /* not necessary but makes this struct more readable */
uint32_t sequence_no;
uint8_t data[8];
uint32_t crc32;
s_mypacket __attribute__((packed));
#pragma pack()
您使用哪种方法取决于您的编译器。您可能还需要支持具有相同头文件的多个不同编译器。这发生在设备和服务器可能完全不同的嵌入式系统中 - 例如,您可能有一个与 x86 Linux 服务器通信的 ARM 设备。
使用打包结构有一些注意事项。最大的问题是您必须避免取消引用成员的地址。在具有多字节对齐字的系统上,这可能会导致未对齐的异常和核心转储。
有些人还会担心性能问题,并认为使用这些打包结构会降低系统速度。确实,在幕后,编译器添加了代码来访问未对齐的数据成员。您可以通过查看 IDE 中的汇编代码来了解这一点。
但是由于打包结构对于通信和数据存储最有用,因此当在内存中使用数据时,可以将数据提取为非打包表示。 通常我们不需要处理内存中的整个数据包。
这里有一些相关的讨论:
pragma pack(1) nor __attribute__ ((aligned (1))) works
Is gcc's __attribute__((packed)) / #pragma pack unsafe?
http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html
【讨论】:
以上是关于uint8_t 与无符号字符的主要内容,如果未能解决你的问题,请参考以下文章