C / C ++中的固定长度数据types
我听说像int
这样的数据types的大小可能会因平台而异。
我的第一个问题是:有人可以带一些例子,什么错误,当程序假定一个int
是4个字节,但在不同的平台上它是2个字节?
我的另一个问题是相关的。 我知道人们用一些typedefs
解决这个问题,就像你有像u8
, u16
, u32
这样的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标准的早期版本中,您通常会根据传入编译器的#define
string(例如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( printf
和scanf
格式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
,则是首选。
首先:不要编写依赖于short
, int
, unsigned int
等types宽度的程序。
基本上:“如果没有标准保证,就不要依赖宽度”。
如果你想成为一个真正的平台独立和存储的价值33000作为一个有符号的整数,你不能只是假设一个int
将持有它。 一个int
至less有-32767
到32767
或-32768
到32767
(取决于一个/二进制补码)。 这是不够的,即使它通常是32位,因此能够存储33000.对于这个值,你肯定需要一个>16bit
types,因此你只需selectint32_t
或int64_t
。 如果这个types不存在,编译器会告诉你这个错误,但这不会是一个沉默的错误。
第二:C ++ 11为固定宽度的整数types提供了一个标准的头文件。 这些都不能保证在你的平台上存在,但是当它们存在时,它们保证是确切的宽度。 请参阅cppreference.com上的这篇文章以获得参考。 types以int[n]_t
和uint[n]_t
的格式命名,其中n
是uint[n]_t
或64
。 您需要包含标题<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_t
, uint8_t
等) – 不同的体系结构之间只有这个头文件是不同的。 所以,在一个架构int16_t
可以是:
typedef int int16_t;
另一个:
typedef short int16_t;
此外,还有其他types,可能是有用的,如: int_least16_t
- 如果一个types比你想象的小,那么它可能不能存储你需要存储的值。
- 要创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位。