C语言 有没有办法在编译时计算整型的宽度?

cx6n0qe3  于 12个月前  发布在  其他
关注(0)|答案(7)|浏览(70)

整数类型(或任何类型)的大小以char/bytes为单位很容易计算为sizeof(type)。一个常见的习惯用法是乘以CHAR_BIT来找到类型占用的位数,但在使用填充位的实现中,这将不等于值位的 width。更糟糕的是,代码如下:

x>>CHAR_BIT*sizeof(type)-1

字符串
如果CHAR_BIT*sizeof(type)大于type的实际宽度,则实际上可能具有未定义的行为。
为了简单起见,让我们假设我们的类型是无符号的。那么type的宽度是ceil(log2((type)-1)。有没有办法将这个值计算为常量表达式?

ulydmbyx

ulydmbyx1#

有一个类似于函数的宏可以确定整数类型的值位,但前提是你已经知道该类型的最大值。是否会得到编译时常量取决于你的编译器,但我猜在大多数情况下答案是肯定的。
感谢Hallvard B. Furuseth在回复question on comp.lang.c时发布的类似IMAX_BITS()函数的宏

/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
                  + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))

字符串
IMAX_BITS(INT_MAX)计算int中的位数,IMAX_BITS((unsigned_type)-1)计算unsigned_type中的位数。除非有人实现4GB整数,否则:-)
感谢Eric Sosman的这个[替代版本](http://groups.google.com/group/comp.lang.c/msg/e998153ef07ff04b?dmode = source),它应该可以在2040位以下工作:(编辑2011年1月3日11:30 PM EST:原来这个版本也是由Hallvard B. Furuseth编写的)

/* Number of bits in inttype_MAX, or in any (1<<k)-1 where 0 <= k < 2040 */
#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))

**请记住,尽管无符号整数类型的宽度等于值位数,但有符号整数类型的宽度大于值位数(§6.2.6.2/6)。**这一点特别重要,因为在我对您的问题的原始评论中,我错误地指出IMAX_BITS()宏在实际计算值位数时计算宽度。对此表示抱歉!

例如IMAX_BITS(INT64_MAX)将创建一个编译时常量63。然而,在这个例子中,我们处理的是一个有符号类型,所以如果你想要一个int64_t的实际宽度,当然是64,你必须加1来解释符号位。
在comp.lang.c的一个单独的讨论中,一个名为blargg的用户给出了宏如何工作的分解:
Re: using pre-processor to count bits in integer types...
请注意,该宏仅适用于2^n-1个值(即二进制中全为1),正如任何MAX值所预期的那样。还要注意,虽然很容易获得无符号整数类型的最大值的编译时常数,(IMAX_BITS((unsigned type)-1)),在写这篇文章的时候,我不知道有什么方法可以在不调用实现的情况下对有符号整数类型做同样的事情-定义的行为。如果我发现了,我会回答我自己的相关问题,在这里:
C question: off_t (and other signed integer types) minimum and maximum values - Stack Overflow

ukqbszuj

ukqbszuj2#

<limits.h>中的宏与特定整数宽度的已知最大值进行比较:

#include <limits.h>

#if UINT_MAX == 0xFFFF
#define INT_WIDTH 16
#elif UINT_MAX == 0xFFFFFF
#define INT_WIDTH 24
#elif ...
#else
#error "unsupported integer width"
#endif

字符串

gcmastyq

gcmastyq3#

第一种方法,如果你知道你有什么标准类型(所以你的类型不是typedef),使用{U}INT_MAX宏并检查可能的大小。
如果你没有这个,对于unsigned类型,这在概念上相对容易。对于你喜欢的类型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 */
.
.
.

gkl3eglg

gkl3eglg4#

你可以在运行时用一个简单的循环计算它,定义良好,没有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;

字符串
最简单的方法是在单元测试中检查value_bits是否与当前的INT_WIDTH定义相同(你有它们,对吗?)。
如果你真的需要在编译时计算它,我会使用给定的#if-#elif级联之一,测试UINT_MAX或你的目标系统。
你要它做什么?也许是雅尼?

wlwcrazw

wlwcrazw5#

一般的观察是,如果你在计算中依赖于数据类型的宽度,你应该使用<stdint.h>中定义的显式宽度数据类型,例如uint32_t
试图计算标准类型中的字节数是在回避一个问题,即在溢出的情况下,您的“可移植”代码会做什么。

yduiuuwa

yduiuuwa6#

是的,因为对于所有实际目的,可能的宽度的数量是有限的:

#if ~0 == 0xFFFF
# define INT_WIDTH 16
#elif ~0 == 0xFFFFFFFF
# define INT_WIDTH 32
#else
# define INT_WIDTH 64
#endif

字符串

goucqfw6

goucqfw67#

通常,int的大小对于给定的编译器/平台是已知的。如果你有标识编译器/平台的宏,那么你可以用它们来有条件地定义INT_WIDTH
您可以查看<sys/types.h>及其依赖项作为示例。

相关问题