如何定义C宏,根据传入变量的值生成变量名(令牌)?
这个需求刚好可以用C预处理器的**令牌拼接(##)**特性来实现,我给你两种不同场景下的实现方案,你可以根据实际需求选择:
基础实现:适用于参数为字面量的场景
如果你的n和f都是直接的字面量(比如0、1、data、status),可以用最简单的宏直接拼接令牌:
#define MEMBER(x, n, f) x->bar##n##_##f
用法示例
#include <stdint.h> struct foo { uint32_t bar0_data; uint32_t bar0_status; uint32_t bar1_data; uint32_t bar1_status; }; int main() { struct foo my_foo = {0}; struct foo *p = &my_foo; // 等价于 p->bar0_data = 0xABCD; MEMBER(p, 0, data) = 0xABCD; // 等价于 uint32_t s = p->bar1_status; uint32_t s = MEMBER(p, 1, status); return 0; }
通用实现:支持参数为宏的场景
如果n或f是通过宏定义的(比如#define BAR_INDEX 0),上面的基础宏会直接把宏名当作令牌拼接,导致错误。这时候需要加一层中间宏来先展开参数,再进行拼接:
// 中间宏:负责实际的令牌拼接 #define MEMBER_CONCAT(prefix, num, field) prefix##num##_##field // 对外暴露的宏:先展开参数,再调用拼接宏 #define MEMBER(x, n, f) x->MEMBER_CONCAT(bar, n, f)
用法示例
#include <stdint.h> #define BAR_INDEX 1 #define FIELD_STATUS status struct foo { uint32_t bar0_data; uint32_t bar0_status; uint32_t bar1_data; uint32_t bar1_status; }; int main() { struct foo my_foo = {0}; struct foo *p = &my_foo; // 等价于 p->bar1_status = 0x1; MEMBER(p, BAR_INDEX, FIELD_STATUS) = 0x1; return 0; }
为什么需要中间宏?
C预处理器有个规则:如果宏参数出现在##令牌旁边,会阻止该参数的预展开。通过中间宏MEMBER_CONCAT,我们让n和f先完成宏展开,再进行令牌拼接,这样就能正确处理参数是宏的情况。
内容的提问来源于stack exchange,提问作者seacoder
相关产品推荐
相关产品推荐

