【发布时间】:2012-09-28 20:18:21
【问题描述】:
每个 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。有什么方法可以说服他?我是第一个想到这个的吗?如果是这样,我能指望有多少处女在天堂等着我呢? :)
【问题讨论】:
-
至于第 (4) 部分,很确定它是 not 72。我认为这是为其他东西保留的值...
-
你不是说
sizeof a[0]吗? -
@dmckee 实际上他很简洁...要切换它们,您需要
(a)[0]以避免可能的宏参数解析错误...尽管*(a)工作正常。 -
@FredOverflow 使用新解决方案查看我的答案中的更新
-
@JoachimPileborg:
a[0] == *(a + 0) == *(0 + a) == 0[a]
标签: c arrays pointers assert sizeof