我听说int
等数据类型的大小可能因平台而异。
我的第一个问题是:有人带来一些例子,出现什么问题,程序时
假设int
是4个字节,但在不同的平台上,它是2个字节?
我遇到的另一个问题是相关的。我知道人们用typedefs
解决了这个问题,
就像你有u8
,u16
,u32
之类的变量 - 保证是8位,16位,32位,无论平台如何 - 我的问题是,这通常是如何实现的? (我不是指stdint
库中的类型 - 我手动很好奇,如何强制某些类型总是说32位而不管平台?)
答案 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
,它为这些新类型(printf
和scanf
格式字符串)的格式转换添加了一些其他简洁的功能。
答案 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)
首先:永远不要编写依赖short
,int
,unsigned int
等类型宽度的程序。
基本上:“如果标准不能保证宽度,则永远不要依赖宽度”。
如果您想要真正独立于平台并存储,例如值33000作为有符号整数,不能假设int
将保留它。 int
至少包含-32767
到32767
或-32768
到32767
的范围(取决于一个/两个补码)。这还不够,即使通常是32位,因此能够存储33000.对于这个值,您最终需要>16bit
类型,因此您只需选择int32_t
或{ {1}}。如果这种类型不存在,编译器会告诉你错误,但它不会是一个无声的错误。
第二:C ++ 11为固定宽度整数类型提供标准头。这些都不能保证在您的平台上存在,但是当它们存在时,它们保证具有确切的宽度。请参阅this article on cppreference.com以获取参考。类型以int64_t
和int[n]_t
格式命名,其中uint[n]_t
为n
,8
,16
或32
。您需要包含标题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_t
,uint8_t
等)的定义方式 - 并且只有不同架构之间的区别才是这个头文件。因此,在一个架构上int16_t
可能是:
typedef int int16_t;
和另一个:
typedef short int16_t;
此外,还有其他类型可能很有用,例如:int_least16_t
答案 7 :(得分: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位。