11

我正在开发几个需要跨多个桌面和移动平台移植的 C 和 C++ 项目。我知道u32_t i64_当我在磁盘上读取和写入数据时,使用显式大小的类型 t 等很重要。

使用所有整数类型的显式大小类型来确保执行一致是个好主意吗?我听说显式大小的类型会对性能产生影响,因为处理器针对它们的预期int类型进行了优化等。我还读到一个好的策略是在内部为类数据成员而不是在接口中使用显式大小的类型。

是否有关于数据成员和接口上显式大小类型的最佳实践?(我假设在这些情况下 C 或 C++ 之间不会有巨大差异,但如果有,请告诉我)

4

4 回答 4

12

基本“int”类型的好处在于,对于您当前正在编译的任何平台,它几乎总是最快的整数类型。

另一方面,使用 int32_t(而不仅仅是 int)的优势在于,无论在什么平台上编译,您的代码都可以依赖始终为 32 位宽的 int32_t,这意味着您可以安全地做出更多假设关于值的行为比使用 int 可以。对于固定大小的类型,如果您的代码完全在新平台 Y 上编译,那么它的行为更有可能与在旧平台 X 上完全相同。

int32_t 的(理论上)缺点是新平台 X 可能不支持 32 位整数(在这种情况下,您的代码根本不会在该平台上编译),或者它可能支持它们但处理它们的速度比处理普通的慢旧整数。

上面的例子有点做作,因为几乎所有现代硬件都在全速处理 32 位整数,但是确实存在(并且确实)存在操作 int64_ts 比操作 int 慢的平台,因为 (a) CPU 具有 32 位寄存器,因此必须将每个操作分成多个步骤,当然 (b) 64 位整数将占用 32 位整数的两倍内存,这会给缓存带来额外压力。

但是:请记住,对于人们编写的 99% 的软件,这个问题不会对性能产生任何明显的影响,因为现在 99% 的软件都不受 CPU 限制,甚至对于也就是说,整数宽度不太可能成为主要的性能问题。所以它真正归结为,你希望你的整数数学表现如何?

  • 如果您希望编译器保证您的整数值始终占用 32 位 RAM,并且始终“环绕”在 2^31(或 2^32 无符号),无论您在什么平台上编译,去与 int32_t (等)。

  • 如果您并不真正关心包装行为(因为您知道整数永远不会包装,因为它们存储的数据的性质),并且您想让代码对于奇怪/不寻常的编译目标更具可移植性,并且至少在理论上更快(尽管在现实生活中可能不是),那么您可以坚持使用普通的旧 short/int/long。

我个人默认使用固定大小的类型(int32_t 等),除非有非常明确的理由不这样做,因为我想尽量减少跨平台的变体行为数量。例如,这段代码:

for (uint32_t i=0; i<4000000000; i++) foo();

...将始终准确地调用 foo() 4000000000 次,而这段代码:

for (unsigned int i=0; i<4000000000; i++) foo();

可能会调用 foo() 4000000000 次,或者它可能会进入无限循环,具体取决于 (sizeof(int)>=4) 与否。当然,可以手动验证第二个代码段在任何给定平台上都不会这样做,但是鉴于两种样式之间的性能差异可能为零,我更喜欢第一种方法,因为预测它的行为是不行的脑筋急转弯。我认为 char/short/int/long 方法在 C 的早期更有用,当时计算机体系结构更加多样化,CPU 足够慢,实现完整的本机性能比安全编码更重要。

于 2012-04-19T21:36:42.267 回答
6

使用inttypes.hstdint.h。它是 ANSI-C,因此任何旨在符合 ANSI 的工具链都将支持它。

此外,它仍然为您节省了重新设计轮子的工作。

你唯一必须做的就是

#include <inttypes.h>

uint32_t integer_32bits_nosign;
  • 关于可移植性的另一个问题:与数据宽度一样重要的是数据字节序。您必须使用标准宏检查目标字节序:

    struct {
    #if defined( __BIG_ENDIAN__ ) || defined( _BIG_ENDIAN )
        // Data disposition for Big Endian   
    #else
        // Data disposition for Little Endian   
    #endif
    };
    

如果您使用位域,它会特别敏感。


编辑:

当然,<csdtint>如果您打算在仅 C++ 的代码上使用它,您可以按照其他人的建议使用它。

于 2012-04-19T18:40:08.223 回答
1

固定大小类型的一个相当讨厌的“陷阱”是,虽然它们给人的印象是代码不依赖于“int”的大小,但这实际上是一种错觉。一段代码如:

uint32_t mul(uint16_t a, uint16_t b)
{ return a*b; }

将对所有“int”为 40 位或更大的平台上的所有“a”和“b”值具有定义的含义,并且还将在所有平台上定义“a”和“b”值的含义,其中“ int" 是 16 位,尽管当算术积为 65535 时含义会有所不同。C89 标准的作者指出,虽然不需要这样做,但那个时代的大多数实现都定义了它们的整数数学行为,使得大多数有符号的操作——除了一些特殊的例外——将与它们的无符号操作相同,即使结果在 INT_MAX+1 和 UINT_MAX 之间--因此在那些编译器上,“a”和“b”的所有值的行为将与具有较大“int”类型的机器上的行为相匹配。然而,对于 32 位编译器来说,生成会以大于 INT_MAX 的值中断的代码已经成为一种时尚,因为标准并没有禁止它们这样做。

于 2016-04-12T07:44:53.247 回答
0

stdint.h 中有 fast_ 类型的大小整数,编译器会选择平台中所需大小的最快整数,例如(从 stdint.h 中提取)

typedef signed char     int_fast8_t;
#if __WORDSIZE == 64
typedef long int        int_fast16_t;
typedef long int        int_fast32_t;
typedef long int        int_fast64_t;
#else
typedef int         int_fast16_t;
typedef int         int_fast32_t;
__extension__
typedef long long int       int_fast64_t;
#endif
于 2012-04-20T01:33:16.193 回答