2

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

#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 高级用户,所以我只是想了解这样做背后是否有任何逻辑。

4

3 回答 3

4

我认为它给你的是宏的用户可以传入任何旧类型的指针,而不仅仅是Result*. 就我个人而言,我要么按照你的方式去做,要么如果我真的想允许(例如)一个void*宏参数,我会写*(Result*)(result) = callResult;.

可能还有另一件事,这取决于宏的其余部分是什么样的。有没有提到“一些额外resultVar的处理”,或者这条线是(*resultVar) = callResult;有条件的?如果是这样,那么resultVar存在是为了确保宏只对其每个参数进行一次评估,因此它的行为更像是一个函数调用,而不是它对它们进行任何其他次数(包括零)的评估。

所以,如果你在一个循环中调用它,HANDLE_CALL(output++, *input++)那么它会做一些模糊可预测的事情。至少,它确实提供outputResult*宏作者的意图。void*除了允许不同的参数类型(如or )之外,我仍然不确定演员会给你什么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真的是一个结构,没有人会真正编写我上面给出的代码。但我认为它可以作为一个例子,为什么宏最终总是比你想象的更复杂。

于 2012-06-12T16:57:33.823 回答
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)左右的所有其他用途都是无用的。

于 2012-06-12T20:28:40.240 回答
0

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

于 2012-06-12T16:55:56.677 回答