为什么这个定义宏执行转换?

时间:2012-06-12 16:52:51

标签: 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用户,所以我只是想了解这背后是否有任何逻辑。

3 个答案:

答案 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)

这完全取决于人们传递给实例的愚蠢行为。你真的需要一个宏 - 一个内联函数会更好。