我有一个类似函数的宏,它接受枚举返回代码和函数调用。
#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 用户,所以我只是想了解这样做背后是否有任何逻辑。
最佳答案
我认为它给你的是,宏的用户可以传入任何旧类型的指针,而不仅仅是Result*
。就我个人而言,我要么按照你的方式去做,或者如果我真的想允许(例如)一个 void*
宏参数,我会写 *(Result*)(result) = callResult ;
.
可能还有另外一种情况,具体取决于宏的其余部分是什么样子。 “一些额外的处理”是否提到 resultVar
,或者行 (*resultVar) = callResult;
是有条件的吗?如果是这样,则 resultVar
的存在是为了确保宏将其每个参数精确地计算一次,因此其行为比它计算它们时更像函数调用任何其他次数(包括零)。
因此,如果您在像 HANDLE_CALL(output++, *input++)
这样的循环中调用它,那么它会执行一些模糊可预测的操作。至少,如果 output
是宏作者预期的 Result*
的话。除了允许不同的参数类型(例如 void*
或 char*
)之外,我仍然不确定强制转换会给您带来什么。
在某些情况下,无论您是否拥有额外的 Actor 阵容,都可能会产生不同的结果。例如,考虑:
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;
,因此进行该更改的原始宏会捕获错误并拒绝代码,而不是做一些令人讨厌的事情。只要 double
能够准确表示 int
的每个值,您的 *result = callResult;
版本实际上可以工作。对于 IEEE double 和小于 53 位的 int
,它可以。
大概 Result
实际上是一个结构,没有人会真正编写我上面给出的代码。但我认为它可以作为一个例子,说明为什么宏最终总是比你想象的更复杂。
关于c - 为什么这个定义宏要执行强制转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11001178/