C++ 宏定义 ARRAY_SIZE
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8018843/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Macro definition ARRAY_SIZE
提问by Lei Mou
I encountered the following macro definition when reading the globals.h in the Google V8project.
我在谷歌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)))))
My question is the latter part: static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
. One thing in my mind is the following: Since the latter part will always evaluates to 1
, which is of type size_t
, the whole expression will be promoted to size_t
.
我的问题是后半部分:static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
. 我想到的一件事如下:由于后半部分将始终评估为1
,它的类型为size_t
,因此整个表达式将被提升为size_t
。
If this assumption is correct, then there comes another question: since the return type of sizeof
operator is size_t, why is such a promotion necessary? What's the benefit of defining a macro in this way?
如果这个假设是正确的,那么又来了一个问题:既然sizeof
operator的返回类型是size_t,为什么还需要这样的提升呢?以这种方式定义宏有什么好处?
采纳答案by iammilind
latter part will always evaluates to 1, which is of type size_t,
后一部分将始终评估为 1,这是 size_t 类型,
Ideally the later part will evaluate to bool
(i.e. true
/false
) and using static_cast<>
, it's converted to size_t
.
理想情况下,后面的部分将评估为bool
(即true
/ false
)并使用static_cast<>
,它将转换为size_t
.
why such promotion is necessary? What's the benefit of defining a macro in this way?
为什么需要这样的推广?以这种方式定义宏有什么好处?
I don't know if this is ideal way to define a macro. However, one inspiration I find is in the comments: //You should only use ARRAY_SIZE on statically allocated arrays.
我不知道这是否是定义宏的理想方式。然而,我在评论中找到了一个灵感://You should only use ARRAY_SIZE on statically allocated arrays.
Suppose, if someone passes a pointerthen it would fail for the struct
(if it's greater than pointer size) data types.
假设,如果有人传递一个指针,那么对于struct
(如果它大于指针大小)数据类型,它将失败。
struct S { int i,j,k,l };
S *p = new S[10];
ARRAY_SIZE(p); // compile time failure !
[Note: This technique may not show any error for int*
, char*
as said.]
[注:此方法可能不会显示任何错误int*
,char*
如说。]
回答by Matthieu M.
As explained, this is a feeble (*) attempt to secure the macro against use with pointers (rather than true arrays) where it would not correctly assess the size of the array. This of course stems from the fact that macros are pure text-based manipulations and have no notion of AST.
正如所解释的,这是一种微弱的 (*) 尝试,以防止宏与指针(而不是真正的数组)一起使用,因为它无法正确评估数组的大小。这当然源于这样一个事实,即宏是纯基于文本的操作,没有AST 的概念。
Since the question is also tagged C++, I would like to point out that C++ offers a type-safe alternative: templates.
由于这个问题也被标记为 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
Alternatively, will soon be able to use constexpr
:
或者,很快就可以使用constexpr
:
template <typename T, size_t N>
constexpr size_t size(T (&)[N]) { return N; }
However my favorite compiler (Clang) still does not implement them :x
但是我最喜欢的编译器(Clang)仍然没有实现它们:x
In both cases, because the function does not accept pointer parameters, you get a compile-time error if the type is not right.
在这两种情况下,由于函数不接受指针参数,如果类型不正确,您将收到编译时错误。
(*) feeble in that it does not work for small objects where the size of the objects is a divisor of the size of a pointer.
(*) 无力,因为它不适用于对象大小是指针大小的除数的小对象。
Just a demonstration that it is a compile-time value:
只是证明它是一个编译时值:
template <size_t N> void print() { std::cout << N << "\n"; }
int main() {
int a[5];
print<ARRAY_SIZE(a)>();
}
See it in action on IDEONE.
在 IDEONE 上查看它的实际效果。
回答by Ben Hymanson
If sizeof(a) / sizeof(*a)
has some remainder (i.e. a
is not an integral number of *a
) then the expression would evaluate to 0
and the compiler would give you a division by zeroerror at compile time.
如果sizeof(a) / sizeof(*a)
有一些余数(即a
不是 的整数*a
),则表达式将计算为0
并且编译器会在编译时给您除以零错误。
I can only assume the author of the macro was burned in the past by something that didn't pass that test.
我只能假设宏的作者过去曾被未通过该测试的东西烧毁。
回答by Omair
In the Linux kernel, the macro is defined as (GCC specific):
在 Linux 内核中,宏定义为(特定于 GCC):
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
where __must_be_array()
is
这里__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]))
and __same_type()
is
并且__same_type()
是
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
回答by Kiril Kirov
The second part wants to ensure that the sizeof( a )
is divisible of by sizeof( *a )
.
第二部分要确保 可sizeof( a )
被 整除sizeof( *a )
。
Thus the (sizeof(a) % sizeof(*(a)))
part. If it's divisible, the expression will be evaluated to 0
. Here comes the !
part - !(0)
will give true
. That's why the cast is needed. Actually, this does not affect the calculation of the size, just adds compile time check.
因此(sizeof(a) % sizeof(*(a)))
部分。如果它是可整除的,则表达式将被评估为0
。来了!
部分 -!(0)
将给true
。这就是为什么需要演员阵容。其实这不影响大小的计算,只是增加了编译时检查。
As it's compile time, in case that (sizeof(a) % sizeof(*(a)))
is not 0
, you'll have a compile-time error for 0 division.
由于它是编译时,如果(sizeof(a) % sizeof(*(a)))
不是0
,您将遇到 0 除法的编译时错误。