问题引入
在工程中结构体数组是一个经常用到的数据结构。数组的长度有时候我们需要用宏定义控制,方便我们在做系列产品的时候随时修改。
数组长度容易控制,直接修改长度宏定义即可。
然而,如果我们在定义结构体的时候使用初始化器做了初始化,初始化器的代码并没有做可伸缩处理,初始化器代码比宏长的时候就会出问题
#define MAX_PORT 1
struct s_port_t port[MAX_PORT] =
{
[0]{
.len = 1,
.data = 0xffed,
.holder = 2,
},
[1]{
.len = 1,
.data = 0xffed,
.holder = 2,
},
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.c|43|error: array index in initializer exceeds array bounds
Build failed: 1 error(s), 1 warning(s) (0 minute(s), 0 second(s))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本文就是为了解决初始化器无法伸缩的问题。
解决办法
先放解决方法,跑一下,下一节分析
#define __CONNECT1__(...) [0]__VA_ARGS__
#define __CONNECT2__(...) [1]__VA_ARGS__,__CONNECT1__(__VA_ARGS__)
#define __CONNECT3__(...) [2]__VA_ARGS__,__CONNECT2__(__VA_ARGS__)
#define __CONNECT4__(...) [3]__VA_ARGS__,__CONNECT3__(__VA_ARGS__)
#define __CONNECT5__(...) [4]__VA_ARGS__,__CONNECT4__(__VA_ARGS__)
#define __CONNECT6__(...) [5]__VA_ARGS__,__CONNECT5__(__VA_ARGS__)
#define __CONNECT7__(...) [6]__VA_ARGS__,__CONNECT6__(__VA_ARGS__)
#define __CONNECT8__(...) [7]__VA_ARGS__,__CONNECT7__(__VA_ARGS__)
/* #define __CONNECT9__...... 续写以支持更多 */
#define __STRUCT_BUNTCH_INIT(__NUM, ...) {__CONNECT##__NUM##__(__VA_ARGS__)}
//套一层,让__NUM先展开为数字
#define STRUCT_BUNTCH_INIT(__NUM, ...) __STRUCT_BUNTCH_INIT(__NUM, __VA_ARGS__)
上面最终创建了STRUCT_BUNTCH_INIT(__NUM, …) 宏,此宏定义第一个参数是要展开的结构体成员长度,后面的…是一个结构体成员的初始化代码。STRUCT_BUNTCH_INIT(__NUM, …) 宏最终会在调用位置展开为__NUM数量的相同的初始化代码。下面是调用示例
struct s_port_t{ //port 结构体
uint8_t len;
uint16_t data;
uint16_t holder;
};
#define MAX_PORT 2
struct s_port_t port[MAX_PORT] = STRUCT_BUNTCH_INIT(
MAX_PORT, /* 结构体参数个数 */
{ /* 每个结构体的初始化代码 */
.len = 1,
.data = 0xffed,
.holder = 2,
}
);
上面将会被展开为如下
//MAX_PORT定义为2:
{
[1]{ .len = 1, .data = 0xffed, .holder = 2, },
[0]{ .len = 1, .data = 0xffed, .holder = 2, }
}
//MAX_PORT定义为3:
{
[2]{ .len = 1, .data = 0xffed, .holder = 2, },
[1]{ .len = 1, .data = 0xffed, .holder = 2, },
[0]{ .len = 1, .data = 0xffed, .holder = 2, }
}
如此,我们便实现了初始化器代码长度可变的目的
宏定义分析
调用__CONNECT1__返回如下
__CONNECT1__(...) [0]__VA_ARGS__
调用__CONNECT2__返回如下
__CONNECT2__(...) [1]__VA_ARGS__,__CONNECT1__(__VA_ARGS__)
展开
__CONNECT2__(...) [1]__VA_ARGS__,[0]__VA_ARGS__
以此类推,调用__CONNECT3____返回如下
__CONNECT3__(...) [2]__VA_ARGS__,[1]__VA_ARGS__,[0]__VA_ARGS__
__STRUCT_BUNTCH_INIT决定调用哪一个CONNECT宏,这里使用到了##粘接宏,用处如下
#define __STRUCT_BUNTCH_INIT(__NUM, ...) {__CONNECT##__NUM##__(__VA_ARGS__)}
__STRUCT_BUNTCH_INIT(1, ...)
展开为
{__CONNECT1__(__VA_ARGS__)}
__STRUCT_BUNTCH_INIT(8, ...)
展开为
{__CONNECT8__(__VA_ARGS__)}
而我们传入_NUM的一般不是数字而是宏定义如MAX_PORT ,我们需要在__STRUCT_BUNTCH_INIT外面套一层STRUCT_BUNTCH_INIT,否则将会被展开为
{__CONNECTMAX_PORT__(__VA_ARGS__)}
最后, […]参数会被传递到__VA_ARGS__中
全部展开,得到如下
__STRUCT_BUNTCH_INIT(3, ...)
展开
{__CONNECT3__(...)}
展开
{
[2]__VA_ARGS__,
[1]__VA_ARGS__,
[0]__VA_ARGS__,
}
结束