每个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]))
这是因为指向数组的指针和指向其第一个元素的指针具有相同的值。如果你传递一个指针,那么这个指针将会与指向它自身的指针进行比较,几乎总是返回false。(唯一的例外是递归的void指针,也就是指向自己的void指针。我可以接受这种情况。)
不小心传递指针而不是数组现在会在运行时触发错误:
Assertion `(void*)&(pointer) == (void*)(pointer)' failed.
不错!现在我有几个问题:
在标准C中,我将
assert
用作逗号表达式的左操作数,这种用法是否有效?也就是说,标准是否允许我将assert
用作表达式?如果这是一个愚蠢的问题,请原谅我 :)是否可以在编译时进行检查?
我的C编译器认为
int b[NUM_ELEMS(a)];
是VLA。有没有什么方法说服它相反呢?我是第一个想到这个问题的人吗?如果是,有多少处女可以等着我在天堂里?:)
sizeof a[0]
? - Some programmer dude(a)[0]
来避免宏参数可能的误解析...不过*(a)
也可以正常工作。 - Jim Balter