c - 为什么这个定义宏要执行强制转换?

标签 c casting macros

我有一个类似函数的宏,它接受枚举返回代码和函数调用。

#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/

相关文章:

c++ - 宏在C++中展开

c - 重新实现 strlen (C)

macros - Clojure 宏中的类型提示 deftype

c - 一个线程写入一个变量,另一个线程读取它

c - 如何将文件中的单词保存到数组中?

c++ - UDT 无法发送无符号字符*

java - 如何将整数值转换为字符串?

sql - TSQL 尝试清理杂乱的字符串字段并将其转换为小数以执行 SUM 函数

c 前序二叉树遍历解释

.net - F# 转换为编译时未知的类型