每个 C 程序员都可以使用这个众所周知的宏来确定数组中元素的数量:
#define NUM_ELEMS(a) (sizeof(a)/sizeof 0[a])
这是一个典型的用例:
int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19};
printf("%lu\n", NUM_ELEMS(numbers)); // 8, as expected
然而,没有什么能阻止程序员意外地传递一个指针而不是一个数组:
int * pointer = numbers;
printf("%lu\n", NUM_ELEMS(pointer));
在我的系统上,这会打印 2,因为显然,指针是整数的两倍。想了想如何防止程序员误传指针,找到了解决办法:
#define NUM_ELEMS(a) (assert((void*)&(a) == (void*)(a)), (sizeof(a)/sizeof 0[a]))
这是有效的,因为指向数组的指针与指向其第一个元素的指针具有相同的值。如果您改为传递指针,则该指针将与指向自身的指针进行比较,这几乎总是错误的。(唯一的例外是递归 void 指针,即指向自身的 void 指针。我可以忍受。)
意外传递指针而不是数组现在会在运行时触发错误:
Assertion `(void*)&(pointer) == (void*)(pointer)' failed.
好的!现在我有几个问题:
我
assert
作为逗号表达式的左操作数的用法是有效的标准 C 吗?也就是说,标准是否允许我assert
用作表达式?对不起,如果这是一个愚蠢的问题:)检查可以在编译时以某种方式完成吗?
我的 C 编译器认为这
int b[NUM_ELEMS(a)];
是一个 VLA。有什么办法让他不信吗?我是第一个想到这个的吗?如果是这样,我能指望有多少处女在天堂等着我呢?:)