我的 C 代码包含许多函数,这些函数将指向不同结构的指针作为参数,这些参数不应该是 NULL 指针。为了使我的代码更具可读性,我决定替换此代码:
if(arg1==NULL || arg2==NULL || arg3==NULL...) {
return SOME_ERROR;
}
使用该宏:
NULL_CHECK(arg1,arg2,...)
如果 args 的数量未知并且它们可以指向不同的结构,我应该如何编写它?(我在 C99 工作)
IMO 最可维护的解决方案是编写多个单独的调用,而不是试图“聪明”地处理它。
例如,Win32 程序员使用 VERIFY 宏,它在调试时运行断言(该宏确保断言从发布代码中删除);看到这样开头的函数并不罕见:
int foo(void* arg1, char* str, int n)
{
VERIFY( arg1 != NULL );
VERIFY( str != NULL );
VERIFY( n > 0 );
显然,您可以很容易地将这 3 行压缩为一行,但如果您不这样做,宏的效果最好。如果你把它们放在不同的行上,那么一个失败的断言会告诉你三个条件中的哪一个没有得到满足,而把它们都放在同一个语句中只会告诉你有什么事情失败了,让你找出其余的。
如果您决定使用宏,那么我建议使用带有单个参数的宏:
#define NULL_CHECK(val) if (val == NULL) return SOME_ERROR;
然后你可以写:
NULL_CHECK(s1.member1);
NULL_CHECK(p2->member2);
等等。其中一个优点是您可以像这样准确地结合错误报告或日志记录来识别第一个无效成员。对于单个复合条件,您只知道其中至少有一个是无效的,但不知道具体是哪一个。
如果您必须处理可变数量的参数,那么您需要研究Boost::Preprocessor
,它适用于 C 和 C++。
并不是说我认为return
在宏中隐藏语句是个好主意,但是这样的宏可以写成:
#define NULL_CHECK(...) \
do { \
void *_p[] = { __VA_ARGS__ }; \
int _i; \
for (_i = 0; _i < sizeof(_p)/sizeof(*_p); _i++) { \
if (_p[_i] == NULL) { \
return SOME_ERROR; \
} \
} \
} while(0)
基本上,将可变参数扩展为一个数组并遍历索引。