C语言 查找表,其内容取决于整数类型的大小



假设一个应该可移植到 LP64 和 ILP32 系统的函数,即long int可以是 32 位或 64 位。 此函数将有一些常量表,但常量本身需要基于类型的宽度。 一个人为的例子:

// Find largest power of 1000 less than x, aka log base 1000 rounded down to an integer
unsigned long int intlog1000l(unsigned long int x) {
const unsigned long int powers[] = {
0, 1000, 1000000, 1000000000,
1000000000000, 1000000000000000, 1000000000000000000 };
unsigned int i;
for (i = 0; i < sizeof(powers)/sizeof(*powers); i++)
if (powers[i] > x) break;
return i - 1; 
}    

如果long int是 64 位,则此代码按预期工作。 但是,如果long int是 32 位,它将失败,因为最后三个常量太大而无法容纳。

解决此问题的一种方法是将函数的接口和表的类型更改为使用uint32_tuint64_t。 但是,如何将其与不使用这些类型的现有 API 结合使用,例如__builtin_clzl()labs()

另一种选择是保持接口相同,但将函数内的参数提升到将支持的最大大小,uint64_t,并保留此大小的表元素存储。 但这在 32 位系统上效率非常低。

可以安排提供一个宏来定义长整数的大小,然后将表的第二行放在#if内。 这很困难,因为预处理器无法使用sizeof()。 需要像 autoconf 这样的东西来确定大小并生成配置标头。 这很难适应现有的构建过程。

在一个打算提供完整的有符号/无符号intlong intlong long int函数中,这里有另一种方法:

unsigned int intlog1000(unsigned int); // assume 32 bits
unsigned long long int intlog1000ll(unsigned long long int); // assume 64 bits
static inline unsigned long int intlog1000l(unsigned long int x)
{ sizeof(x) == sizeof(unsigned int) ? intlog1000(x) : intlog1000ll(x); }

这使得假设可以安全地假设 int 和 long long int 是一定的大小,并且 long int 的大小将等于一个或另一个。 因为当前存在的几乎每个 32 位平台都是这种情况。

有没有更好的方法?

考虑一系列#if

#include <limits.h>
const unsigned long int powers[] = {
0, 1000, 1000000, 1000000000
#if ULONG_MAX/1000 >= 1000000000
, 1000000000000u
#endif
#if ULONG_MAX/1000 >= 1000000000000
, 1000000000000000u
#endif
#if ULONG_MAX/1000 >= 1000000000000000
, 1000000000000000000u
#endif
};

上述方法确实存在麻烦,因为有时会对"宏数学"进行签名(基于经验,而不是规范),因此下面的代码合理地假设最大unsigned long约为最大signed long的 2 倍。 "嵌套"方法更好,因为它确保"宏观数学"工作,因为它取决于先前的成功。 这对于 C99,11 编译器来说并不那么重要,因为数学至少是 64 位。 这与较旧的编译器或想要将此方案扩展到比 64 位unsigned long更宽的编译器会有所不同。

#if LONG_MAX/1000 >= 500000000
, 1000000000000u
#if LONG_MAX/1000 >= 500000000000
, 1000000000000000u
#if LONG_MAX/1000 >= 5000000000000000
, 1000000000000000000u
#if LONG_MAX/1000 >= 5000000000000000000
#error powers[] needs extending
#endif
#endif
#endif
#endif

"宏数学"或更好的预处理器算术,在 C11(可能还有 C99)中至少用 64 位数学完成,但在早期的 C89 中只有至少 32 位。

出于此令牌转换和计算的目的,所有有符号整数类型和所有无符号整数类型的行为就好像它们分别具有与标头<stdint.h>中定义的intmax_tuintmax_t相同的表示形式。这包括解释字符常量,这可能涉及将转义序列转换为执行字符集成员。 C11 §6.10.1 4

您可以考虑为表元素提供类型unsigned long long intuintmax_t(这可能是也可能不是一回事)。 任何符合 C99 或 C11 的实现都将提供至少 64 位宽的unsigned long long int,因此uintmax_t也至少如此宽。

当然,这就引出了一个问题,即您的函数如何处理大于 64 位的输入。

相关内容

最新更新