#define DLT645_READ_FLOAT_TO_DOUBLE(CHAN, FUNC, DOUBLE_DEST) \
{ \
float float_val; \
if (chan_dlt645_read_data(CHAN, FUNC, (unsigned char*)&float_val) < 0) { \
-1; \
} else { \
DOUBLE_DEST = float_val; \
0; \
} \
}
分析:
-
宏的返回值:宏并没有真正的返回值的概念,所以在
-1;
和0;
这两个地方,它们并不会像函数一样被返回。您可能的意图是希望在chan_dlt645_read_data
函数调用失败的时候返回-1,成功的时候返回0,但是这样的写法并不能达成这个目的。 -
不适当的大括号使用:该宏定义的开始和结束处出现了大括号,这可能在某些使用场景下会导致问题。例如在if...else...语句中使用该宏可能会引发语法错误。
改进:
RESULT
是由用户提供的变量,用来接收返回的结果。同时,添加了do...while(0)
结构,可以避免宏在某些场景下的使用出现问题。最后,我们使用(double)
对float_val
进行了强制类型转换,以确保将其赋值给DOUBLE_DEST
时不会出现问题。
#define DLT645_READ_FLOAT_TO_DOUBLE(CHAN, FUNC, DOUBLE_DEST, RESULT) \
do { \
float float_val; \
if (chan_dlt645_read_data(CHAN, FUNC, (unsigned char*)&float_val) < 0) { \
RESULT = -1; \
} else { \
DOUBLE_DEST = (double)float_val; \
RESULT = 0; \
} \
} while(0)
为什么要使用do...while(0)
结构?
如果不使用do...while(0)
结构,那么在使用这个宏的时候可能会碰到问题。这是因为预编译器在预处理宏的时候,会直接进行文本替换,不会添加额外的;(分号)或者 {}(大括号)
下面是一个例子:
if (condition)
DLT645_READ_FLOAT_TO_DOUBLE(CHAN, FUNC, DOUBLE_DEST, RESULT);
else
// other code
在这个例子中,如果你的宏定义不使用do...while(0)
结构,在预编译器进行宏替换之后,代码变为:
if (condition)
float float_val;
if (chan_dlt645_read_data(CHAN, FUNC, (unsigned char*)&float_val) < 0)
RESULT = -1;
else {
DOUBLE_DEST = (double)float_val;
RESULT = 0;
};
else
// other code
这样的代码会导致编译错误,因为if只控制到了第一句,其他的代码都在if的控制范围之外。
但是如果我们使用do...while(0)
,通过预编译器的替换之后,代码变为:
if (condition)
do {
float float_val;
if (chan_dlt645_read_data(CHAN, FUNC, (unsigned char*)&float_val) < 0)
RESULT = -1;
else {
DOUBLE_DEST = (double)float_val;
RESULT = 0;
}
} while(0);
else
// other code
这样的代码不会有编译错误。所以,使用do...while(0)
可以提高宏的安全性,让宏在各种场景中都能正确工作。