在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)))))
我的问题是后半部分:static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
。我想到的一件事是:由于后一部分将始终评估为1
size_t
,因此整个表达式将被提升为size_t
。
如果这个假设是正确的,那么还有另一个问题:由于sizeof
运算符的返回类型是size_t,为什么需要这样的推广?以这种方式定义宏的好处是什么?
答案 0 :(得分: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
或者,很快就可以使用constexpr
:
template <typename T, size_t N>
constexpr size_t size(T (&)[N]) { return N; }
但是我最喜欢的编译器(Clang)仍然没有实现它们:x
在这两种情况下,由于函数不接受指针参数,如果类型不正确,则会出现编译时错误。
(*)微弱,因为它不适用于小物体,其中物体的大小是指针大小的除数。
只是演示它是编译时的值:
template <size_t N> void print() { std::cout << N << "\n"; }
int main() {
int a[5];
print<ARRAY_SIZE(a)>();
}
答案 1 :(得分:10)
后一部分将始终计算为1,其类型为size_t,
理想情况下,后一部分将评估为bool
(即true
/ false
)并使用static_cast<>
,它会转换为size_t
。
为什么需要这种促销?定义a的好处是什么? 宏以这种方式?
我不知道这是否是定义宏的理想方式。但是,我发现的一个灵感来自评论://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 !
[注意:此技术可能不会显示int*
,char*
的任何错误。]
答案 2 :(得分:8)
如果sizeof(a) / sizeof(*a)
有一些余数(即a
不是*a
的整数),则表达式将计算为0
,编译器会给你一个< em>在编译时将 错误除以。
我只能假设宏的作者过去曾被那些没有通过测试的东西烧掉。
答案 3 :(得分:5)
在Linux内核中,宏定义为(GCC特定):
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
其中__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]))
和__same_type()
是
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
答案 4 :(得分:2)
第二部分希望确保sizeof( a )
可被sizeof( *a )
整除。
因此(sizeof(a) % sizeof(*(a)))
部分。如果它可以被整除,表达式将被评估为0
。 !
部分 - !(0)
将提供true
。这就是演员阵容需要的原因。实际上,这不会影响大小的计算,只需添加编译时检查。
由于它是编译时间,如果(sizeof(a) % sizeof(*(a)))
不是0
,你将有0分区的编译时错误。