C/C + + 中的固定长度数据类型

我听说数据类型(如 int)的大小可能因平台而异。

我的第一个问题是: 有人能带来一些例子,什么出错了,当程序 假设一个 int是4字节,但是在另一个平台上它是2字节?

我还有一个问题是相关的。我知道人们用 typedefs来解决这个问题, 像 u8u16u32这样的变量,无论平台如何,它们都保证是8比特,16比特,32比特,我的问题是,这通常是如何实现的?(我不是指 stdint库中的类型——我很好奇,不管平台如何,怎样才能强制某些类型总是说32位?)

15678 次浏览

在 C 标准的早期迭代中,您通常创建自己的 typedef语句,以确保获得(例如)基于传递到编译器的 #define字符串的16位类型,例如:

gcc -DINT16_IS_LONG ...

现在(C99及以上) ,有一些特定的类型,比如 uint16_t,这是一个16位宽的无符号整数。

如果包含 stdint.h,则可以得到精确的位宽类型,至少是该宽类型,具有给定最小宽度的最快类型等等,如 C99 7.18 Integer types <stdint.h>中所记录的。如果实现具有兼容类型,则需要它们提供这些类型。

同样非常有用的是 inttypes.h,它为这些新类型(printfscanf格式字符串)的格式转换添加了一些其他简洁的特性。

首先: 不要编写依赖于 shortintunsigned int等类型宽度的程序。

基本上: “如果没有标准的保证,就不要依赖宽度”。

如果您想要真正独立于平台并将值33000作为一个有符号整数存储,那么 不行只需假设 int将保存它。int至少具有从 -3276732767或从 -3276832767的范围(取决于1/2个补体)。这是不够的,即使它的 int0是32位,因此能够存储33000。对于这个值,您肯定需要 >16bit类型,因此您只需选择 int32_tint64_t。如果此类型不存在,编译器将告诉您错误,但这不会是无声的错误。

第二: C + + 11为固定宽度的整数类型提供了一个标准头。这些都不能保证在您的平台上存在,但是当它们存在时,它们的确切宽度是有保证的。有关参考资料,请参阅 uint[n]_t0。这些类型以 int[n]_tuint[n]_t的格式命名,其中 n8163264。您需要包含头 <cstdint>C报头当然是 <stdint.h>

  1. 如果类型比您想象的要小,那么它可能无法存储您需要在其中存储的值。
  2. 要创建固定大小的类型,您需要阅读要支持的平台的文档,然后基于 #ifdef为特定平台定义 typedef

第一个问题: 整数溢出

对于第二个问题: 例如,对于 typedef一个无符号的32位整数,在 int为4字节的平台上,使用:

 typedef unsigned int u32;

int为2字节而 long为4字节的平台上:

typedef unsigned long u32;

这样,您只需修改一个头文件,就可以使类型跨平台。

如果存在一些特定于平台的宏,则无需手动修改即可实现:

#if defined(PLAT1)
typedef unsigned int u32;
#elif defined(PLAT2)
typedef unsigned long u32;
#endif

如果支持 C99stdint.h,则首选。

我很好奇,无论平台如何,怎样才能强制某种类型总是说32位?

如果您希望(现代的) C + + 程序的编译失败,如果给定的类型不是您所期望的宽度,请在某处添加 static_assert。我会在对字体宽度做出假设的地方加上这个。

static_assert(sizeof(int) == 4, "Expected int to be four chars wide but it was not.");

最常用的平台上的 chars是8位大,但并非所有平台都是这样工作的。

第一个例子,大概是这样:

int a = 45000; // both a and b
int b = 40000; // does not fit in 2 bytes.
int c = a + b; // overflows on 16bits, but not on 32bits

如果您查看 cstdint头文件,您将发现所有固定大小类型(int8_tuint8_t等)是如何定义的-不同体系结构之间唯一的不同就是这个头文件。因此,在一种架构上,int16_t可以是:

 typedef int int16_t;

另一方面:

 typedef short int16_t;

此外,还有其他类型,这可能是有用的,如: int_least16_t

有人能举个例子吗,当程序假设 int 是4字节,但在不同的平台上是2字节时,出了什么问题?

假设你设计的程序可以读取100,000个输入,并且假设它的大小为32位(32位无符号整数可以计数到4,294,967,295) ,你使用 unsigned int来计数。如果你用16位整数(16位无符号整数只能计数到65,535)在一个平台(或编译器)上编译代码,由于容量的原因,这个值会绕过65535,表示一个错误的计数。

通常情况下,问题发生在您最大化的数字或当您序列化。一个不太常见的情况发生在某人做出明确的大小假设时。

在第一种情况下:

int x = 32000;
int y = 32000;
int z = x+y;        // can cause overflow for 2 bytes, but not 4

在第二种情况下,

struct header {
int magic;
int w;
int h;
};

然后有人写道:

header h;
// fill in h
fwrite(&h, sizeof(h), 1, fp);


// this is all fine and good until one freads from an architecture with a different int size

在第三种情况下:

int* x = new int[100];
char* buff = (char*)x;




// now try to change the 3rd element of x via buff assuming int size of 2
*((int*)(buff+2*2)) = 100;


// (of course, it's easy to fix this with sizeof(int))

如果您正在使用一个相对较新的编译器,我会使用 uint8 _ t、 int8 _ t 等。为了保证型号的大小。

在较老的编译器中,typedef 通常是在每个平台的基础上定义的:

 #ifdef _WIN32
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
// and so on...
#endif

通过这种方式,每个平台都会有一个标题,定义该平台的具体细节。

编译器有责任遵守标准。当你包括 <cstdint><stdint.h>时,他们应该根据标准尺寸提供类型。

编译器知道他们正在为什么样的平台编译代码,然后他们可以生成一些内部宏或魔法来构建合适的类型。例如,32位机器上的编译器生成 __32BIT__宏,以前它在 stdint头文件中有以下行:

#ifdef __32BIT__
typedef __int32_internal__ int32_t;
typedef __int64_internal__ int64_t;
...
#endif

你可以利用它。

我知道人们用一些 typedef 来解决这个问题,比如你有像 u8,u16,u32这样的变量——它们保证是8位,16位,32位,不管平台如何

有些平台没有特定大小的类型(例如 TI 的28xxx,其中 char 的大小为16位)。在这种情况下,不可能有8位类型(除非您真的想要它,但这可能会导致性能下降)。

这通常是怎么做到的?

通常对于 typedefs. c99(和 c + + 11)有 这些 typedef 在标题中

有人能举个例子吗,当程序假设 int 是4字节,但在不同的平台上是2字节时,出了什么问题?

最好的例子是具有不同类型大小的系统之间的通信。将 int 数组从一个平台发送到另一个平台,其中 sizeof (int)在两个平台上是不同的,因此必须非常小心。

另外,将 int 数组保存在32位平台上的二进制文件中,并在64位平台上重新解释它。

位标志是一个简单的例子。0x10000会给你带来麻烦,你不能用它来掩盖或者检查一个位是否设置在第17位,如果所有的东西都被截断或者粉碎以适应16位。