有没有办法在编译时计算整数类型的宽度?
以char
/ bytes为单位的整数类型(或任何类型)的大小很容易计算为sizeof(type)
。 常见的习惯用法是乘以CHAR_BIT
以找到该类型占用的位数,但是在使用填充位的实现中,这将不等于值位的宽度 。 更糟糕的是,代码如下:
x>>CHAR_BIT*sizeof(type)-1
如果CHAR_BIT*sizeof(type)
大于CHAR_BIT*sizeof(type)
的实际宽度,则实际上可能具有未定义的行为。
为简单起见,我们假设我们的类型是无符号的。 那么type
的宽度是ceil(log2((type)-1)
。有没有办法将这个值计算为常量表达式?
有一个类似函数的宏可以确定整数类型的****值位****,但前提是你已经知道该类型的最大值。 你是否会得到一个编译时常量取决于你的编译器,但我猜在大多数情况下答案是肯定的。
感谢Hallvard B. Furuseth在回复comp.lang.c上的一个问题时发布的类似IMAX_BITS()函数的宏。
/* Number of bits in inttype_MAX, or in any (1<
IMAX_BITS(INT_MAX)计算int中的位数,IMAX_BITS((unsigned_type)-1)计算unsigned_type中的位数。 直到有人实现4千兆字节的整数,无论如何:-)
感谢Eric Sosman这个备用版本应该使用少于2040位:
(编辑2011年1月3日美国东部时间下午11:30:事实certificate这个版本也是由Hallvard B. Furuseth撰写的)
/* Number of bits in inttype_MAX, or in any (1<
请记住,虽然无符号整数类型的宽度等于值位数,但有符号整数类型的宽度更大(§6.2.6.2/ 6)。 这是特别重要的,因为在我对您的问题的原始评论中,我错误地声明IMAX_BITS()宏在实际计算值位数时计算宽度。 对于那个很抱歉!
因此,例如IMAX_BITS(INT64_MAX)
将创建63的编译时常量。但是在此示例中,我们正在处理签名类型,因此如果您想要int64_t的实际宽度,则必须为符号位添加1,当然是64。
在另一个comp.lang.c讨论中,名为blargg的用户给出了宏如何工作的细分:
Re:使用预处理器来计算整数类型的位数...
请注意,宏仅适用于2 ^ n-1个值(即二进制中的所有1),正如任何MAX值所预期的那样。 另请注意,虽然很容易获得无符号整数类型的最大值的编译时常量( IMAX_BITS((unsigned type)-1)
),但在撰写本文时我不知道如何做对于有符号整数类型,同样的事情是不调用实现定义的行为。 如果我发现我会回答我自己的相关问题,请点击此处:
C问题:off_t(和其他有符号整数类型)最小值和最大值 - Stack Overflow
将
中的宏与特定整数宽度的已知最大值进行比较:
#include #if UINT_MAX == 0xFFFF #define INT_WIDTH 16 #elif UINT_MAX == 0xFFFFFF #define INT_WIDTH 24 #elif ... #else #error "unsupported integer width" #endif
第一种方法,如果你知道你有什么标准类型(所以你的类型没有typedef
),请使用{U}INT_MAX
宏并检查可能的大小。
如果你没有这个,对于无符号类型,这在概念上相对容易。 对于你最喜欢的类型T
,只需做(T)-1
并做一个怪物测试宏,用?:
检查所有可能的值。 因为这些只是编译时常量表达式,所以任何体面的编译器都会优化它,只留下您感兴趣的值。
由于类型转换,这在#if
等中不起作用,但这不能以简单的方式避免。
对于签名类型,这更复杂。 对于至少与int
一样宽的类型,您可以希望做一个技巧来提升到相应的无符号类型,然后获得该类型的宽度。 但要知道你的签名类型是否只有一个值或更少,我不认为有一个通用的表达式来知道。
编辑:为了说明这一点,我给出一些你可以做的提取,使这种方法(对于无符号类型)不会在P99中生成太大的表达式我有类似的东西
#ifndef P99_HIGH2 # if P99_UINTMAX_WIDTH == 64 # define P99_HIGH2(X) \ ((((X) & P00_B0) ? P00_S0 : 0u) \ | (((X) & P00_B1) ? P00_S1 : 0u) \ | (((X) & P00_B2) ? P00_S2 : 0u) \ | (((X) & P00_B3) ? P00_S3 : 0u) \ | (((X) & P00_B4) ? P00_S4 : 0u) \ | (((X) & P00_B5) ? P00_S5 : 0u)) # endif #endif #ifndef P99_HIGH2 # if P99_UINTMAX_WIDTH <= 128 # define P99_HIGH2(X) \ ((((X) & P00_B0) ? P00_S0 : 0u) \ | (((X) & P00_B1) ? P00_S1 : 0u) \ | (((X) & P00_B2) ? P00_S2 : 0u) \ | (((X) & P00_B3) ? P00_S3 : 0u) \ | (((X) & P00_B4) ? P00_S4 : 0u) \ | (((X) & P00_B5) ? P00_S5 : 0u) \ | (((X) & P00_B6) ? P00_S6 : 0u)) # endif #endif
其中魔术常数在开头用#if序列定义。 重要的是不要为无法处理它们的编译器暴露太大的常量。
/* The preprocessor always computes with the precision of uintmax_t */ /* so for the preprocessor this is equivalent to UINTMAX_MAX */ #define P00_UNSIGNED_MAX ~0u #define P00_S0 0x01 #define P00_S1 0x02 #define P00_S2 0x04 #define P00_S3 0x08 #define P00_S4 0x10 #define P00_S5 0x20 #define P00_S6 0x40 /* This has to be such ugly #if/#else to ensure that the */ /* preprocessor never sees a constant that is too large. */ #ifndef P99_UINTMAX_MAX # if P00_UNSIGNED_MAX == 0xFFFFFFFFFFFFFFFF # define P99_UINTMAX_WIDTH 64 # define P99_UINTMAX_MAX 0xFFFFFFFFFFFFFFFFU # define P00_B0 0xAAAAAAAAAAAAAAAAU # define P00_B1 0xCCCCCCCCCCCCCCCCU # define P00_B2 0xF0F0F0F0F0F0F0F0U # define P00_B3 0xFF00FF00FF00FF00U # define P00_B4 0xFFFF0000FFFF0000U # define P00_B5 0xFFFFFFFF00000000U # define P00_B6 0x0U # endif /* P00_UNSIGNED_MAX */ #endif /* P99_UINTMAX_MAX */ #ifndef P99_UINTMAX_MAX # if P00_UNSIGNED_MAX == 0x1FFFFFFFFFFFFFFFF # define P99_UINTMAX_WIDTH 65 # define P99_UINTMAX_MAX 0x1FFFFFFFFFFFFFFFFU # define P00_B0 0xAAAAAAAAAAAAAAAAU # define P00_B1 0xCCCCCCCCCCCCCCCCU # define P00_B2 0xF0F0F0F0F0F0F0F0U # define P00_B3 0xFF00FF00FF00FF00U # define P00_B4 0xFFFF0000FFFF0000U # define P00_B5 0xFFFFFFFF00000000U # define P00_B6 0x10000000000000000U # endif /* P00_UNSIGNED_MAX */ #endif /* P99_UINTMAX_MAX */ . . .
是的,因为出于所有实际目的,可能的宽度数量是有限的:
#if ~0 == 0xFFFF # define INT_WIDTH 16 #elif ~0 == 0xFFFFFFFF # define INT_WIDTH 32 #else # define INT_WIDTH 64 #endif
您可以在运行时使用一个简单的循环计算它,定义明确且没有UB的危险:
unsigned int u; int c; for (c=0, u=1; u; c++, u<<=1); total_bits = CHAR_BIT * sizeof(unsigned int); value_bits = c; padding_bits = total_bits - value_bits;
最简单的方法是检查你的unit testing(你有它们,对吗?)value_bits与你当前的INT_WIDTH定义相同。
如果你真的需要在编译时计算它,我会选择一个给定的#if- #elif级联,测试UINT_MAX或你的目标系统。
你需要什么? 也许是YAGNI?
一般观察是,如果在计算中依赖于数据类型的宽度,则应使用
定义的显式宽度数据类型,例如uint32_t
。
尝试计算标准类型中的字节数就是问题,即“溢出”时你的“可移植”代码会做什么。
通常, int
大小对于给定的编译器/平台是已知的。 如果您有识别编译器/平台的宏,那么您可以使用它们来条件定义INT_WIDTH
。
您可以查看
及其依赖项以获取示例。