有没有办法在编译时计算整数类型的宽度?

时间:2010-10-18 07:30:34

标签: c integer width padding

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

x>>CHAR_BIT*sizeof(type)-1
如果CHAR_BIT*sizeof(type)大于type的实际宽度,

实际上可能会有未定义的行为。

为简单起见,我们假设我们的类型是无符号的。然后type的宽度为ceil(log2((type)-1)。有没有办法将此值计算为常量表达式?

7 个答案:

答案 0 :(得分:10)

有一个类似函数的宏可以确定整数类型的****值位****,但前提是你已经知道该类型的最大值。你是否会得到一个编译时常量取决于你的编译器,但我想在大多数情况下答案是肯定的。

感谢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中的位数。直到有人实现4千兆字节的整数,无论​​如何: - )


对于alternate version这个应该小于2040位的Re: using pre-processor to count bits in integer types...归功于Eric Sosman
(编辑2011年1月3日美国东部时间下午11:30:事实证明这个版本也是由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的编译时常量。但是在此示例中,我们正在处理签名类型,因此如果您想要实际的宽度,则必须为符号位添加1一个int64_t,当然是64.

在另一个comp.lang.c讨论中,名为blargg的用户给出了宏如何工作的细分:
C question: off_t (and other signed integer types) minimum and maximum values - Stack Overflow

请注意,宏仅适用于2 ^ n-1个值(即二进制中的所有1),正如任何MAX值所预期的那样。另请注意,虽然很容易获得无符号整数类型(IMAX_BITS((unsigned type)-1))的最大值的编译时常量,但在撰写本文时,我不知道是否有任何方法可以执行相同的操作有符号整数类型,不调用实现定义的行为。如果我发现我会回答我自己的相关问题,请点击此处:
{{3}}

答案 1 :(得分:5)

<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

答案 2 :(得分:2)

第一种方法,如果您知道自己的标准类型(因此类型为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 */
.
.
.

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

您可以在运行时使用一个简单的循环计算它,定义明确且没有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或你的目标系统。

你需要什么?也许是YAGNI?

答案 5 :(得分:0)

一般观察是,如果您在计算中依赖数据类型的宽度,则应使用<stdint.h>中定义的显式宽度数据类型,例如uint32_t

尝试计算标准类型中的字节数就是在发生溢出时你的“可移植”代码会做什么的问题。

答案 6 :(得分:-1)

通常,int的大小对于给定的编译器/平台是已知的。如果您有识别编译器/平台的宏,那么您可以使用它们来条件定义INT_WIDTH

您可以查看<sys/types.h>及其家属的示例。