在C中使用uint8_t优于unsigned char的优势是什么?

我知道在几乎每个系统uint8_t只是一个类型定义的unsigned char, 那么为什么要用它呢?


当前回答

就像你说的,“几乎每个系统”。

Char可能是一个不太可能改变的,但一旦你开始使用uint16_t和朋友,使用uint8_t混合更好,甚至可能成为编码标准的一部分。

其他回答

根据我的经验,有两个地方,我们想用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设备。

使用打包结构有几个注意事项。最大的问题是必须避免对成员的地址进行解引用。在使用多字节对齐单词的系统上,这可能导致不对齐异常和coredump。

有些人还会担心性能,并认为使用这些打包结构会降低系统的速度。的确,在幕后,编译器会添加代码来访问未对齐的数据成员。您可以通过查看IDE中的汇编代码来了解这一点。

但是由于打包结构对通信和数据存储最有用,因此当在内存中使用数据时,可以将数据提取为非打包表示。 通常情况下,我们不需要处理内存中的整个数据包。

以下是一些相关讨论:

Pragma pack(1)或__attribute__ ((aligned(1))都有效

gcc的__attribute__((wrapped)) / #pragma包不安全吗?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html

它记录了你的意图——你将存储小数字,而不是一个字符。

此外,它看起来更好,如果你使用其他类型,如uint16_t或int32_t。

只是为了卖弄学问,有些系统可能没有8位类型。根据维基百科:

实现需要为N = 8,16,32或64定义精确宽度的整数类型,当且仅当它具有满足要求的任何类型时。不需要为任何其他N定义它们,即使它支持适当的类型。

所以uint8_t并不保证存在,尽管它将为所有平台,其中8位= 1字节。一些嵌入式平台可能有所不同,但这种情况越来越少。一些系统可能将char类型定义为16位,在这种情况下可能不会有任何8位类型。

除了这个(小)问题,@Mark Ransom的答案在我看来是最好的。使用最清楚地显示你使用数据的目的的那个。

此外,我假设你的意思是uint8_t(标准类型定义从C99提供的stdint.h头)而不是uint_8(不是任何标准的一部分)。

关键在于编写与实现无关的代码。Unsigned char不能保证是8位类型。Uint8_t是(如果可用)。

在几乎每个系统上,我都遇到了uint8_t == unsigned char,但这不是由C标准保证的。如果你试图编写可移植的代码,它的确切大小是什么内存,使用uint8_t。否则使用unsigned char。