我有一个类似函数的宏,它接受枚举返回码和函数调用。
#define HANDLE_CALL(result,call) \
do { \
Result callResult = call; \
Result* resultVar = (Result*)result; \
// some additional processing
(*resultVar) = callResult; \
} while(0)
花哨的指针转换为Result*
并随后取消引用会获得什么吗?也就是说,这样做有什么好处:
callResult = call;
// additional processing
*result = callResult;
宏的使用方式如下:
Result result;
HANDLE_CALL(&result,some_function());
顺便说一句,这不是我的代码而且我不是强大的C用户,所以我只是想了解这背后是否有任何逻辑。
答案 0 :(得分:4)
我认为它给你的是,宏的用户可以传入任何旧类型的指针,而不仅仅是Result*
。就个人而言,我要么按照自己的方式去做,要么我真的想允许(例如)void*
宏参数我写*(Result*)(result) = callResult;
。
还有可能的另一件事,具体取决于宏的其余部分。 “一些额外处理”是否完全提及resultVar
,或者行(*resultVar) = callResult;
是否有条件?如果是这样,则resultVar
存在,以确保宏一次评估其每个参数完全,因此它的行为更像是一个函数调用,而不是评估任何其他数字时间(包括零)。
所以,如果你在像HANDLE_CALL(output++, *input++)
这样的循环中调用它,那么它可以做一些模糊的预测。至少,它确实提供了output
是宏作者所期望的Result*
。除了允许void*
或char*
等不同的参数类型之外,我仍然不确定演员给你的是什么。
在某些情况下,无论你是否有额外的演员阵容,都可能会产生另一个差异。例如,考虑:
typedef double Result;
int foo() { return 1; }
int i;
HANDLE_CALL(&i, foo());
如果屏幕上看不到typedef double Result;
,则其他三行看起来非常无害。将int赋给int有什么问题,对吧?但是一旦宏被扩展,当你将int*
转换为double*
时就会发生不好的事情。使用强制转换,这些不良内容是未定义的行为,很可能double
大于int
并且它超出了i
的内存。如果你很幸运,你会得到关于“严格别名”的编译器警告。
如果没有强制转换,则无法执行double* resultVar = &i;
,因此具有该更改的原始宏会捕获错误并拒绝代码,而不是执行令人讨厌的操作。如果*result = callResult;
可以准确地表示double
的每个值,则int
的版本确实有效。对于IEEE双倍和int
小于53位,它可以。
据推测,Result
实际上是一个结构,没有人会真正编写我上面给出的代码。但我认为这可以作为一个例子,为什么宏总是比你想象的更加繁琐。
答案 1 :(得分:1)
正如史蒂夫所说,演员阵容能够将不同于Result*
的内容传递给宏。但我认为它不应该做演员,这是危险的,但只是初始化。更好的是
#define HANDLE_CALL(result,call) \
do { \
Result callResult = (call); \
Result* resultVar = (result); \
/* some additional processing */ \
(*resultVar) = callResult; \
} while(0)
这会强制result
分配与Result*
兼容,因此它可以是Result*
或void*
。强制转换(指向更大结构的指针)到(它的第一个类型Result
)字段的所有其他用途都是无用的玩火。
答案 2 :(得分:0)
这完全取决于人们传递给实例的愚蠢行为。你真的需要一个宏 - 一个内联函数会更好。