宏定义ARRAY_SIZE

Lei*_*Mou 13 c c++ macros

在Google V8项目中读取globals.h时遇到以下宏定义.

// The expression ARRAY_SIZE(a) is a compile-time constant of type
// size_t which represents the number of elements of the given
// array. You should only use ARRAY_SIZE on statically allocated
// arrays.

#define ARRAY_SIZE(a)                               \
  ((sizeof(a) / sizeof(*(a))) /                     \
  static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
Run Code Online (Sandbox Code Playgroud)

我的问题是后半部分:static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))).我想到的一件事是:由于后一部分将始终求1值为(类型)size_t,整个表达式将被提升为size_t.

如果这个假设是正确的,那么还有另一个问题:由于sizeof运算符的返回类型是size_t,为什么需要这样的推广?以这种方式定义宏有什么好处?

Mat*_* M. 11

正如所解释的那样,这是一种微弱的(*)尝试来保护宏不被指针(而不是真正的数组)使用,因为它不能正确地评估数组的大小.这当然源于这样一个事实:宏是基于纯文本的操作,并且没有AST的概念.

由于问题也标记为C++,我想指出C++提供了一种类型安全的替代方案:模板.

#ifdef __cplusplus
   template <size_t N> struct ArraySizeHelper { char _[N]; };

   template <typename T, size_t N>
   ArraySizeHelper<N> makeArraySizeHelper(T(&)[N]);

#  define ARRAY_SIZE(a)  sizeof(makeArraySizeHelper(a))
#else
#  // C definition as shown in Google's code
#endif
Run Code Online (Sandbox Code Playgroud)

或者,很快就能使用constexpr:

template <typename T, size_t N>
constexpr size_t size(T (&)[N]) { return N; }
Run Code Online (Sandbox Code Playgroud)

但是我最喜欢的编译器(Clang)仍然没有实现它们:x

在这两种情况下,由于函数不接受指针参数,如果类型不正确,则会出现编译时错误.

(*)微弱,因为它不适用于小对象,其中对象的大小是指针大小的除数.


只是演示它是一个编译时的值:

template <size_t N> void print() { std::cout << N << "\n"; }

int main() {
  int a[5];
  print<ARRAY_SIZE(a)>();
}
Run Code Online (Sandbox Code Playgroud)

在IDEONE上查看它的实际操作.


iam*_*ind 10

后一部分将始终计算为1,其类型为size_t,

理想情况下,后面的部分将评估bool(即true/ false)和使用static_cast<>,它被转换为size_t.

为什么这种促销是必要的?以这种方式定义宏有什么好处?

我不知道这是否是定义宏的理想方式.但是,我发现的一个灵感来自评论://You should only use ARRAY_SIZE on statically allocated arrays.

假设,如果有人传递指针,那么它将失败struct(如果它大于指针大小)数据类型.

struct S { int i,j,k,l };
S *p = new S[10];
ARRAY_SIZE(p); // compile time failure !
Run Code Online (Sandbox Code Playgroud)

[注:此方法可能不会显示任何错误int*,char*如说.]

  • 可以在这里找到略有不同的解释:http://stackoverflow.com/questions/1598773/is-there-a-standard-function-in-c-that-would-return-the-length-of-an-array/1598827#1598827 (2认同)

Ben*_*son 8

如果sizeof(a) / sizeof(*a)有一些剩余(即a不是整数*a),那么想要得到0和编译器会给你一个被零除错误在编译时.

我只能假设宏的作者过去曾被没有通过该测试的东西烧毁.


Oma*_*air 5

在Linux内核中,宏定义为(特定于GCC):

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
Run Code Online (Sandbox Code Playgroud)

这里__must_be_array()

/* &a[0] degrades to a pointer: a different type from an array */
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
Run Code Online (Sandbox Code Playgroud)

并且__same_type()

#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
Run Code Online (Sandbox Code Playgroud)

  • 最后一部分(__must_be_array())比较有趣。 (2认同)