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


74

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

我的第一个问题是:有人可以举个例子吗,当程序假设anint是4个字节,但是在另一个平台上却是2个字节时,出了什么问题?

我遇到的另一个问题是相关的。我知道人们可以通过某些方法来解决此问题typedefs,例如,您拥有像u8,,-这样的变量,无论使用什么平台u16u32该变量都保证是8bit,16bit,32bits-我的问题是,通常如何实现?(我不是指stdint库中的类型-我是很好奇的手动工具,无论平台如何,一个类型如何总是说32位?)

c++  c 

3
覆盖内存存在潜在问题。如果您假设一个整数在另一个平台上为2字节时为4字节,则根据内存的布局方式,可以覆盖整数之后的下2个字节。
奥斯汀·布伦霍斯特

9
是阅读Usenet时代的C常见问题解答的好时机:faqs.org/faqs/C-faq/abridged,然后faqs.org/faqs/C-faq/faq(未删节,所以如果您可以改为阅读该信息!更多信息)。它讨论了许多这样的假设,也谈到了许多其他经常错误的假设(NULL的内部表示等)。(必须阅读的一章是关于null以及指针/数组的内容。其余的内容也不错,并且在许多主题
Olivier Dulac

1
请注意,字节顺序可能因平台而异。(+1的问题-最好提出问题,然后假设“肯定sizeof(void *)会一直存在4。” )
Maciej Piechotka

@MaciejPiechotka:同意。最好将这些内容发布出来,因为许多读者可能会意识到潜在的陷阱及其解决方案!没有不好的问题[好吧,如果他们能提供足够的背景信息],只是不好的回答^^
Olivier Dulac

Answers:


41

我知道人们可以使用一些typedef解决此问题,例如您拥有u8,u16,u32之类的变量-无论平台如何,该变量都保证为8bits,16bits,32bits

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

通常如何实现?

通常使用typedefs。c99(和c ++ 11)在header中具有这些typedef。因此,只需使用它们。

有人可以举个例子吗,当程序假设一个int是4个字节,但是在另一个平台上却说是2个字节时,出了什么问题呢?

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

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


14
+1用于将整数数组保存在32位平台上的二进制文件中,并在64位平台上重新解释它。
legends2k

22

在C标准的较早版本中,通常typedef基于#define传递到编译器中的字符串来做出自己的声明,以确保获得(例如)16位类型,例如:

gcc -DINT16_IS_LONG ...

如今(C99及更高版本),有一些特定类型,例如uint16_t,恰好16位宽的无符号整数。

包括在内stdint.h,您将获得确切的位宽类型,最小宽度类型,具有给定最小宽度的最快类型等等,如中所述C99 7.18 Integer types <stdint.h>。如果实现具有兼容类型,则要求它们提供这些类型。

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


1
子问题:如果平台不支持16位整数类型,则unint16_t未在cstdintetc ..中定义?还是标准保证类型将始终存在(并在内部做一些事情以确保其有效)?
马丁·约克

5
不,仅当实现具有兼容类型时,C标准才需要它。如果你在一个12位DSP例如运行,那么它不具备提供16位uint16_t。它可能但不是强制性的:7.18.1.1/3: These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a two’s complement representation, it shall define the corresponding typedef names.
paxdiablo

4
因此,如果您使用uint16_t并且平台不支持它,那么在移植过程中可能会出现编译错误。
马丁·约克

1
@Loki,是的,编译器不会知道类型。
paxdiablo

16

第一个问题:整数溢出

对于第二个问题:例如,typedefint4字节的平台上,使用32位无符号整数,请使用:

 typedef unsigned int u32;

int2字节而long4字节的平台上:

typedef unsigned long u32;

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

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

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

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


没关系,有这样的时间...-休息一下!
2013年

这里有什么平台?是硬件(例如x86,x86_64,AMD等)还是操作系统(例如Solaris,AIX,HP-UX,Linux,macOS,BSD和IBM z / OS等)?
Darshan L

8

首先:不要写程序,依靠类型的宽度一样shortintunsigned int,...

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

如果要真正独立于平台并将例如33000的值存储为有符号整数,则不能仅假设anint将容纳它。Anint至少具有-32767to32767-32768to的范围32767(取决于1/2的补码)。即使通常为32位,因此能够存储33000,这还不够。对于该值,您肯定需要一个>16bit类型,因此您只需选择int32_tor即可int64_t。如果不存在此类型,则编译器将告诉您错误,但这不会是无提示的错误。

第二:C ++ 11为固定宽度的整数类型提供了标准标头。这些都不保证可以在您的平台上存在,但是当它们存在时,可以保证它们具有完全相同的宽度。有关参考,请参见cppreference.com上的本文。该类型的格式命名int[n]_t,并uint[n]_t在那里n8163264。您需要包含标头<cstdint>。该C头是当然的<stdint.h>


2
OP:“我不是从stdint库中引用类型-我很好奇,无论平台如何,一个类型如何总是说32位?
legends2k

2
@ legends2k具有固定宽度整数类型的正确方法使用标准库。
stefan

4
同意,但是那是您编写代码的时间,而不是当您尝试学习如何首先编写此类标头时。
legends2k

7
首先:永远不要编写依赖于类型宽度的程序。 ”因此,您是说我们不应该依赖于uint32_t32位宽吗?抽象是不错的选择,但最终所有这些都会使您需要做出一些假设才能真正完成所有工作。
托马斯

6
您是什么意思,“永远不要编写依赖于类型宽度的程序”?类型的宽度直接影响可能值的范围,这在选择使用哪种类型时非常重要,尤其是对于许多人使用C / C ++进行编程的任务而言。如果要编写文件系统或需要在受约束的内存中存储大量值的任何东西,则需要做出此类决定。原因是字符串不存储为unsigned long long数组。
tfinniga

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

然后去写:

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

这样,每个平台都有一个标头,用于定义该平台的详细信息。


2
+1是第一个提到的结构。您还应该知道通过网络发送strct时会发生什么。
James Anderson

5

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

如果您希望(现代)C ++程序的编译在给定类型不符合您的期望宽度时失败,请在static_assert某处添加一个。我将在关于类型宽度的假设周围添加此内容。

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

chars 在最常用的平台上,它的大小为8位,但并非所有平台都以这种方式工作。


3
sizeof实际上返回的大小以chars为单位,而不是字节。因此,如果要按位检查大小,则应该这样做sizeof(int) * CHAR_BIT == 32
user694733 2013年

static_assert仅在最新标准中可用。但是uint_32t和类似的类型可以从以前获得
山姆

@ user694733编号。按定义,以字符为单位的大小=以字节为单位的大小。sizeof(char)==1–总是。
康拉德·鲁道夫

与@sammy Nopeuint32_t等同时添加static_assert
康拉德·鲁道夫

@KonradRudolph这取决于字节的定义。字节通常被认为是8位。char总是有CHAR_BIT位。CHAR_BIT至少为8,但可能更多。
user694733 2013年

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


2
  1. 如果类型比您想象的要小,则它可能无法存储您需要在其中存储的值。
  2. 要创建一个固定大小类型,你读得到支持的平台的文档,然后定义typedef基于S上#ifdef针对特定平台。

2

有人可以举个例子吗,当程序假设一个int是4个字节,但是在另一个平台上却说是2个字节时,出了什么问题呢?

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


1

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

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

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

您可以使用它。


0

位标志就是简单的例子。0x10000会给您带来问题,您不能使用它进行遮罩,也无法检查是否将第17位设置为1,如果所有内容都被截断或粉碎以适合16位。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.