简化legacy代码时遇到表达式必须为可修改左值问题如何解决
错误原因
标准C规范中,三目运算符?:的返回结果为右值,不具备可修改的内存地址,因此不能放在等号左侧执行赋值操作,这就是你遇到左值报错的根本原因。
最优解决方案(兼容所有C标准,无编译器依赖,性能和原手写代码完全一致)
把赋值逻辑封装到宏内部,用do{}while(0)的标准宏写法避免语法问题:
头文件宏定义
#define LOG_AMBIENT_LOW_TEMP(SELECT_DEVICE, VAL) do { \ #if dev_a if ((SELECT_DEVICE) == 0x00) query.warning.bit.lowtemp_dev_a = (VAL); \ #endif #if dev_b else if ((SELECT_DEVICE) == 0x02) query.warning.bit.lowtemp_dev_b = (VAL); \ #endif #if dev_c else query.warning.bit.lowtemp_dev_c = (VAL); \ #endif } while(0)
调用方式
uint16_t Device = DEVSEL; /* 从其他位置解析得到 */ LOG_AMBIENT_LOW_TEMP(Device, 1U); // 单行调用,效果和原手写代码完全一致
- 宏内的
do{}while(0)是C语言多行宏的标准写法,编译后会被完全优化掉,不会引入额外执行开销,同时可以避免宏在if/else等分支语句中展开时出现语法错误 - 宏内保留了你原有的
dev_a/dev_b/dev_c编译期开关,编译后生成的代码和你原来手动写的多段#if代码完全等价,没有冗余判断
可选方案(仅支持GNU C扩展的编译器)
如果你需要保留「宏放在等号左侧赋值」的写法,可以用GCC/Clang支持的语句表达式扩展实现:
#define LOG_AMBIENT_LOW_TEMP(SELECT_DEVICE) ({ \ typeof(query.warning.bit.lowtemp_dev_a) *ret; \ #if dev_a if ((SELECT_DEVICE) == 0x00) ret = &query.warning.bit.lowtemp_dev_a; \ #endif #if dev_b else if ((SELECT_DEVICE) == 0x02) ret = &query.warning.bit.lowtemp_dev_b; \ #endif #if dev_c else ret = &query.warning.bit.lowtemp_dev_c; \ #endif *ret; \ })
该写法返回左值,你原来的调用方式LOG_AMBIENT_LOW_TEMP(Device) = 1U;可以直接运行,但无法在不支持GNU扩展的编译器下使用。
内容的提问来源于stack exchange,提问作者EmbeddedMaker
相关产品推荐
相关产品推荐

