通常教授的标准数组大小宏是

#define ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))

或者其他类似的构造.然而,当一个指针被传入时,这种事情会悄悄地成功,并给出在运行时看似合理的结果,直到事情神秘地崩溃.

犯这个错误太容易了:一个具有局部数组变量的函数被重构,将一点数组操作移动到一个以数组作为参数调用的新函数中.

所以,问题是:是否有一个"卫生"宏来检测C中ARRAYSIZE宏的滥用,最好是在编译时?在C++中,我们只使用专门用于数组参数的模板;在C语言中,我们似乎需要一些方法来区分数组和指针.(例如,如果我想拒绝数组,我只会这样做,例如(arr=arr, ...),因为数组分配是非法的).

推荐答案

Linux内核使用了一个很好的ARRAY_SIZE实现来解决这个问题:

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))

具有

#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

Of course this is portable only in GNU C as it makes use of two instrinsics: typeof operator 和 __builtin_types_compatible_p function. Also it uses their "famous" BUILD_BUG_ON_ZERO macro which is only valid in GNU C.

假设有编译时求值要求(这正是我们想要的),我不知道这个宏的任何可移植实现.

A "semi-portable" implementation (和 which would not cover all cases) is:

#define ARRAY_SIZE(arr)  \
    (sizeof(arr) / sizeof((arr)[0]) + STATIC_EXP(IS_ARRAY(arr)))

具有

#define IS_ARRAY(arr)  ((void*)&(arr) == &(arr)[0])
#define STATIC_EXP(e)  \
    (0 * sizeof (struct { int ARRAY_SIZE_FAILED:(2 * (e) - 1);}))

With gcc this gives no warning if argument is an array in -std=c99 -Wall but -pedantic would gives a warning. The reason is IS_ARRAY expression is not an integer constant expression (cast to pointer types 和 subscript operator are not allowed in integer constant expressions) 和 the bit-field width in STATIC_EXP requires an integer constant expression.

C++相关问答推荐

如何将一个integer与一个数组进行比较?

函数指针始终为零,但在解除引用和调用时有效

InetPton()函数无效的IP地址

GCC预处理宏和#杂注GCC展开

为什么GCC C23中的关键字FALSE不是整数常量表达式?

在 struct 中强制转换空指针

Linux不想运行编译后的文件

我的程序在收到SIGUSR1信号以从PAUSE()继续程序时总是崩溃()

如何在ASM中访问C struct 成员

如何使用[BTStack]BLE发送大型(>;2kb)信息包

如何使解释器存储变量

Vcpkg的配置文件

For循环中的变量行为不符合预期.[C17]

如何确保我将使用C标准库函数的函数版本,如&getc";,而不是类似函数的宏版本?

链接器脚本和C程序使用相同的头文件,这可能吗?

我正在使用c学习数据 struct ,在学习堆栈时,我试图将中缀转换为后缀,并编写了这段代码.代码未给出输出

为什么argc和argv即使在主函数之外也能工作?

GCC认为这是一个VLA是对的吗?

一元运算符

为什么程序在打印每个数字之前要等待所有输入?