标准C环境下支持空指针参数的FOO宏改进方案咨询
标准C环境下支持空指针参数的FOO宏改进方案咨询
我现在在写C代码,需要直接调用接收数组参数的函数,比如这个:
int foo(size_t length, const int array[/* length */]);
手动跟踪数组长度实在太容易出错了,所以我写了个包装宏,用大家常用的数组长度宏来自动计算长度:
#define ARRAY_LENGTH(array) ( sizeof(array) / sizeof *(array) ) #define FOO(array) foo(ARRAY_LENGTH(array), array)
// 复合字面量外面必须加括号,避免逗号被解析成宏参数的分隔符
平时我是这么用的:
FOO(((int[]){1, 2, 3})); FOO(((int[]){4, 5}));
现在遇到个问题:有时候我需要表示一个概念上的空数组。我不想用(int[]){}这种写法,因为零长度数组是GCC和clang的扩展,在MSVC下根本编译不过。我更倾向于用空指针——这样函数里能更容易检测到非法解引用的情况,毕竟解引用空指针通常会直接崩溃,方便排查问题。
所以我想优化我的FOO宏,让它既能接受普通数组(包括复合字面量数组),也能接受空指针参数,并且遇到空指针时自动把长度设为0。关键是,这个方案必须在标准C里工作,不能依赖编译器扩展。
我自己试了两种思路,但都有问题:
思路1:用C11的_Generic检测NULL类型
我想借助C11的_Generic,通过类型来识别NULL:
#define NULLSAFE_LEN(xs) (_Generic((xs), \ void *: (size_t)0, \ default: ARRAY_LENGTH(xs) \ )) printf("%zu\n", NULLSAFE_LEN(NULL)); // 期望输出0
但尴尬的是,宏的所有分支都会被求值,哪怕最终不会被选中。这就导致GCC和clang会弹出警告(提示sizeof (((void *)0))得到的是指针大小而非数组大小),而MSVC直接编译失败——因为标准C里sizeof(void)是不允许的。
思路2:用C23的__VA_OPT__实现宏重载
我还考虑过用C23的__VA_OPT__,根据参数数量来重载FOO宏:
#define FOO_0() foo(0, NULL) #define FOO_1(arr) foo(ARRAY_LENGTH(arr), arr) #define SELECT_MACRO(zero, one, macro_name, ...) macro_name #define FOO(...) SELECT_MACRO(__VA_OPT__(,) __VA_ARGS__, FOO_1, FOO_0)(__VA_ARGS__)
用起来是这样的:
FOO(); // 展开为 foo(0, NULL); FOO(((int[]){1, 2, 3})); // 展开为 foo(3, (int[]){1,2,3});
这个方案本身能工作,但C23的支持度还不够广泛,我没法在项目里依赖它。而且FOO();这种写法看起来像是调用无参函数,有点违背直觉,容易让人困惑。
有没有什么其他的标准C方案能实现我想要的功能?
内容来源于stack exchange
相关产品推荐
相关产品推荐

