C / C ++中的固定长度数据types

我听说像int这样的数据types的大小可能会因平台而异。

我的第一个问题是:有人可以带一些例子,什么错误,当程序假定一个int是4个字节,但在不同的平台上它是2个字节?

我的另一个问题是相关的。 我知道人们用一些typedefs解决这个问题,就像你有像u8u16u32这样的variables – 无论平台如何都保证是8位,16位,32位 – 我的问题是,这是怎么实现的? (我不是指stdint库中的types – 我手动好奇,如何强制某种types总是说32位而不pipe平台是什么?)

我知道人们用一些types定义来解决这个问题,比如你有像u8,u16,u32这样的variables – 无论平台如何,它们都是8位,16位,32位

有一些平台,没有一定的大小types(例如TI的28xxx,char的大小是16位)。 在这种情况下,不可能有8位的types(除非你真的想要它,但是这可能会引入性能问题)。

这通常是如何实现的?

通常使用typedefs。 c99(和c ++ 11) 在标题中有这些types定义 。 所以,只要使用它们。

有人可以举一些例子,什么错误,当程序假定一个int是4字节,但在不同的平台上,它是2字节?

最好的例子是不同types的系统之间的通信。 将int数组从一个发送到另一个平台,其中sizeof(int)在两个平台上是不同的,必须格外小心。

此外,还要在32位平台上保存二进制文件中的整数,然后在64位平台上对其进行重新解释。

在C标准的早期版本中,您通常会根据传入编译器的#definestring(例如typedef自定义typedef语句,以确保您获得(例如)16位types:

 gcc -DINT16_IS_LONG ... 

现在(C99及以上),有一些特定的types,如uint16_t ,正好是16位宽的无符号整数。

如果包含stdint.h ,则可以获得精确的位宽types,至less具有这种宽度的types,具有给定最小宽度等的最快types,如C99 7.18 Integer types <stdint.h> 。 如果实现具有兼容types,则需要提供这些types。

另外非常有用的是inttypes.h ,它为这些新types( printfscanf格式string)的格式转换添加了一些其他整洁的function。

对于第一个问题: 整数溢出 。

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

  typedef unsigned int u32; 

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

 typedef unsigned long u32; 

这样,你只需要修改一个头文件就可以使这个types跨平台。

如果有一些平台特定的macros,可以在不手动修改的情况下实现:

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

如果支持C99 stdint.h ,则是首选。

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

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

如果你想成为一个真正的平台独立和存储的价值33000作为一个有符号的整数,你不能只是假设一个int将持有它。 一个int至less有-3276732767-3276832767 (取决于一个/二进制补码)。 这是不够的,即使它通常是32位,因此能够存储33000.对于这个值,你肯定需要一个>16bittypes,因此你只需selectint32_tint64_t 。 如果这个types不存在,编译器会告诉你这个错误,但这不会是一个沉默的错误。

第二:C ++ 11为固定宽度的整数types提供了一个标准的头文件。 这些都不能保证在你的平台上存在,但是当它们存在时,它们保证是确切的宽度。 请参阅cppreference.com上的这篇文章以获得参考。 types以int[n]_tuint[n]_t的格式命名,其中nuint[n]_t64 。 您需要包含标题<cstdint>C头文件当然是<stdint.h>

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

在第一种情况下:

 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; }; 

那么一个去fwrite:

 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等来保证types的大小。

在较老的编译器中,typedef通常是以每个平台为基础定义的。 例如,可以这样做:

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

通过这种方式,每个平台将会有一个头文件来定义该平台的细节。

我很好奇手动,如何强制执行一些types总是说32位,无论平台?

如果您希望(现代)C ++程序的编译失败,如果给定的types不是您期望的宽度,请在某处添加static_assert 。 我会添加这个关于types宽度的假设。

 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头文件,您将会发现如何定义所有固定大小的types( int8_tuint8_t等) – 不同的体系结构之间只有这个头文件是不同的。 所以,在一个架构int16_t可以是:

  typedef int int16_t; 

另一个:

  typedef short int16_t; 

此外,还有其他types,可能是有用的,如: int_least16_t

  1. 如果一个types比你想象的小,那么它可能不能存储你需要存储的值。
  2. 要创build固定大小的types,请阅读要支持的平台的文档,然后基于特定平台的#ifdef定义typedef

有人可以举一些例子,什么错误,当程序假定一个int是4字节,但在不同的平台上,它是2字节?

假设你已经devise了你的程序来读取100,000个input,并且你使用一个32位大小的无符号整数(32位无符号整数可以计数到4,294,967,295)来对它进行计数。 如果使用16位整数(16位无符号整数只能计数到65,535)在平台(或编译器)上编译代码,由于容量的原因该值将环绕65535以上并表示计数错误。

编译器负责遵守标准。 当包含<cstdint><stdint.h> ,应按标准大小提供types。

编译器知道他们正在编译什么平台的代码,然后他们可以生成一些内部的macros或魔法来构build合适的types。 例如,32位机器上的编译器会生成__32BIT__macros,并且之前在stdint头文件中包含这些行:

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

你可以使用它。

位标志是一个微不足道的例子。 0x10000会导致你的问题,你不能掩盖它,或者如果一切都被截断或破坏,以适应16位,检查是否设置在第17位。