gpt4 book ai didi

c - 可靠地确定数组中元素的数量

转载 作者:太空狗 更新时间:2023-10-29 16:32:34 25 4
gpt4 key购买 nike

每个 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]))

这是有效的,因为指向数组的指针与指向其第一个元素的指针具有相同的值。如果您改为传递一个指针,该指针将与指向其自身的指针进行比较,这几乎总是错误的。 (唯一的异常(exception)是递归 void 指针,即指向自身的 void 指针。我可以接受。)

不小心传递指针而不是数组现在会在运行时触发错误:

Assertion `(void*)&(pointer) == (void*)(pointer)' failed.

不错!现在我有几个问题:

  1. 我使用 assert 作为逗号表达式的左操作数是否有效标准 C?也就是说,标准是否允许我使用 assert 作为表达式?对不起,如果这是一个愚蠢的问题:)

  2. 能否以某种方式在编译时完成检查?

  3. 我的 C 编译器认为 int b[NUM_ELEMS(a)]; 是一个 VLA。有什么办法可以说服他吗?

  4. 我是第一个想到这个的吗?如果是这样,我能指望有多少处女在天堂等着我? :)

最佳答案

Is my usage of assert as the left operand of the comma expression valid standard C? That is, does the standard allow me to use assert as an expression?

是的,它是有效的,因为逗号运算符的左操作数可以是 void 类型的表达式。 assert 函数的返回类型为 void

My C compiler thinks that int b[NUM_ELEMS(a)]; is a VLA. Any way to convince him otherwise?

之所以这么认为,是因为逗号表达式的结果永远不是常量表达式(例如,1、2 不是常量表达式)。

EDIT1:在下方添加更新。

我有另一个在编译时运行的宏版本:

#define NUM_ELEMS(arr)                                                 \
(sizeof (struct {int not_an_array:((void*)&(arr) == &(arr)[0]);}) * 0 \
+ sizeof (arr) / sizeof (*(arr)))

而且它似乎甚至可以与具有静态存储持续时间的对象的初始化程序一起使用。它也可以与您的 int b[NUM_ELEMS(a)]

示例一起正常工作

编辑 2:

地址@DanielFischer评论。上面的宏适用于 gcc without -pedantic 只是因为 gcc 接受:

(void *) &arr == arr

作为整数常量表达式,同时它考虑

(void *) &ptr == ptr

不是整型常量表达式。根据 C,它们都不是整数常量表达式,并且使用 -pedanticgcc 在这两种情况下都能正确发出诊断。

据我所知,没有 100% 可移植的方法来编写此 NUM_ELEM 宏。 C 在初始化常量表达式方面有更灵活的规则(请参阅 C99 中的 6.6p7),可以利用这些规则来编写此宏(例如使用 sizeof 和复合文字),但在 block 范围内 C 不需要初始化器成为常量表达式,因此不可能有一个适用于所有情况的宏。

编辑 3:

我认为值得一提的是,Linux 内核有一个 ARRAY_SIZE 宏(在 include/linux/kernel.h 中)在稀疏(内核静态分析检查器)被执行。

他们的解决方案不可移植并且使用了两个 GNU 扩展:

  • typeof 运算符
  • __builtin_types_compatible_p 内置函数

基本上它看起来像这样:

#define NUM_ELEMS(arr)  \
(sizeof(struct {int :-!!(__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0])));}) \
+ sizeof (arr) / sizeof (*(arr)))

关于c - 可靠地确定数组中元素的数量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12784136/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com