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

时间:2013-11-27 08:40:17

标签: c++ c

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

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

我遇到的另一个问题是相关的。我知道人们用typedefs解决了这个问题, 就像你有u8u16u32之类的变量 - 保证是8位,16位,32位,无论平台如何 - 我的问题是,这通常是如何实现的? (我不是指stdint库中的类型 - 我手动很好奇,如何强制某些类型总是说32位而不管平台?)

11 个答案:

答案 0 :(得分:41)

  

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

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

  

这通常是如何实现的?

通常使用typedef。 c99(和c ++ 11)有these typedefs in a header。所以,只需使用它们。

  

有人可以带一些例子,出现什么问题,程序假定int是4个字节,但是在不同的平台上说2个字节?

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

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

答案 1 :(得分:22)

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

gcc -DINT16_IS_LONG ...

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

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

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

答案 2 :(得分:16)

对于第一个问题:Integer Overflow

对于第二个问题:例如,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

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

答案 3 :(得分:7)

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

基本上:“如果标准不能保证宽度,则永远不要依赖宽度”。

如果您想要真正独立于平台并存储,例如值33000作为有符号整数,不能假设int将保留它。 int至少包含-3276732767-3276832767的范围(取决于一个/两个补码)。这还不够,即使通常是32位,因此能够存储33000.对于这个值,您最终需要>16bit类型,因此您只需选择int32_t或{ {1}}。如果这种类型不存在,编译器会告诉你错误,但它不会是一个无声的错误。

第二:C ++ 11为固定宽度整数类型提供标准头。这些都不能保证在您的平台上存在,但是当它们存在时,它们保证具有确切的宽度。请参阅this article on cppreference.com以获取参考。类型以int64_tint[n]_t格式命名,其中uint[n]_tn81632。您需要包含标题64<cstdint>标题当然是C

答案 4 :(得分:6)

通常,当您最大数量或序列化时会出现问题。当有人做出明确的大小假设时,会发生一种不常见的情况。

在第一个场景中:

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等,以确保类型大小。

在较旧的编译器中,typedef通常基于每个平台定义。例如,可以做:

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

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

答案 5 :(得分:5)

  

我很好奇手动,如何强制某些类型总是说32位而不管平台?

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

static_assert(sizeof(int) == 4, "Expected int to be four chars wide but it was not.");
最常用平台上的

chars大8位,但并非所有平台都以这种方式工作。

答案 6 :(得分:3)

嗯,第一个例子 - 像这样:

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

答案 7 :(得分:2)

  1. 如果某个类型比您想象的要小,则可能无法存储您需要存储的值。
  2. 要创建固定大小类型,请阅读要支持的平台的文档,然后根据特定平台的typedef定义#ifdef

答案 8 :(得分:2)

  
    

有人可以带一些例子,出现什么问题,程序假定int是4个字节,但是在不同的平台上说2个字节?

  

假设您已将程序设计为读取100,000个输入,并且假设大小为32位(32位无符号整数可计为4,294,967,295),则使用unsigned int对其进行计数。如果在具有16位整数的平台(或编译器)上编译代码(16位无符号整数只能计数到65,535),由于容量的原因,该值将环绕经过65535并表示错误的计数。

答案 9 :(得分:1)

编制者有责任遵守该标准。当您加入<cstdint><stdint.h>时,他们应根据标准尺寸提供类型。

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

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

你可以使用它。

答案 10 :(得分:0)

位标志是一个简单的例子。 0x10000会导致您出现问题,您无法使用它进行掩码或检查是否在第17个位置设置了一个位,如果所有内容都被截断或粉碎以适合16位。