我在头文件中有一个这样的结构。
typedef struct test
{
int a;
int b[10];
int c[5][6];
}testStruct;
我在另一个函数中分别对元素进行了初始化,如下图所示。
void foo()
{
testStruct t[2];
t[0].a = 10;
for(int i = 0; i < 10; i++)
{
t[0].b[i] = i;
}
for(int i = 0; i <5; i++)
{
for(int j =0; j < 6; j++)
{
t[0].c[i][j] = j;
}
}
这种初始化是可行的。但是我在一个嵌入式C项目中使用这个函数。而且我遇到了RAM问题,因为这个结构体占用了很大的空间。在寻找解决方案后,我发现我可以将变量放在ROM中,让它们成为常量。但是我无法将这个结构体变成常量。我找到的一个方法是像下面这样将结构体初始化为常量。
const testStruct t2 = { 0, {1,2 ..}, {3.4...} }
问题是,我不知道如何初始化数组元素。因为在我的项目中,数组的大小达到了100多个元素,我想知道是否有办法将数组元素初始化。我想知道是否有办法可以初始化这个结构,并使它成为一个常量,这样它就会被存储在ROM中。
感谢任何帮助:)
在C语言中,你不能简单地在初始化常量时做循环,你应该找到另一种方法来解决这个问题。我可以建议两种解决方案。
这通常是通过 "硬编码 "来完成的。如果初始化器很复杂,初始化器的C源可以由外部脚本生成。
在某些情况下,你可能也可以用预处理器来解决,但这并不是一个理想的任务工具,因为它使代码难以阅读。还有,要想想出这种各种形式的针对项目的 "聪明 "宏,你需要相当丰富的C语言知识和晦涩难懂的语言特性,再加上 "跳出框框 "的思维方式。
以你的情况为例,你需要数字序列,你就需要 可以 编写一些宏,比如这些。
#define SEQ1 0,
#define SEQ2 SEQ1 1,
#define SEQ3 SEQ2 2,
#define SEQ4 SEQ3 3,
#define SEQ5 SEQ4 4,
#define SEQ6 SEQ5 5,
#define SEQ7 SEQ6 6,
#define SEQ8 SEQ7 7,
#define SEQ9 SEQ8 8,
#define SEQ10 SEQ9 9,
#define SEQUENCE(n) {SEQ##n}
然后你可以像这样初始化结构。
const testStruct t[2] =
{
[0] =
{
.a = 10,
.b = SEQUENCE(10),
.c = { SEQUENCE(6),SEQUENCE(6),SEQUENCE(6),SEQUENCE(6),SEQUENCE(6) },
},
};
另一个技巧是用一个宏来创建一个联合体,比如说
#define SEQUENCE_TYPE(n) typedef union { int arr_max[10]; int arr[n]; } sequence_trick;
然后
static const sequence_trick trick = { .arr_max=(int[]){0,1,2,3,4,5,6,7,8,9} };
trick.arr // here you have a const array of length n, compile-time initialized to values 0,1,..., n
你已经用normaltypicaleasy的方法做了?
typedef struct test
{
int a;
int b[10];
int c[5][6];
}testStruct;
const testStruct t2 = { 0, {1,2 ..}, {3.4...} }
不知道我是否理解这个问题,因为你已经实现了答案。
#define SECOND_PASS
#include <stdio.h>
typedef struct test
{
int a;
int b[10];
int c[5][6];
}testStruct;
testStruct t[2];
#ifdef SECOND_PASS
const testStruct test[2]=
{
{/*0*/
/*a*/10,
{/*b*/0,1,2,3,4,5,6,7,8,9,},
{/*c*/
{/*0*/0,1,2,3,4,5,},
{/*1*/1,2,3,4,5,6,},
{/*2*/2,3,4,5,6,7,},
{/*3*/3,4,5,6,7,8,},
{/*4*/4,5,6,7,8,9,},
}/*c*/,
}/*0*/,
{/*1*/
/*a*/11,
{/*b*/1,2,3,4,5,6,7,8,9,10,},
{/*c*/
{/*0*/1,2,3,4,5,6,},
{/*1*/2,3,4,5,6,7,},
{/*2*/3,4,5,6,7,8,},
{/*3*/4,5,6,7,8,9,},
{/*4*/5,6,7,8,9,10,},
}/*c*/,
}/*1*/,
};
#endif
int main ( void )
{
unsigned int ra;
for(ra=0;ra<2;ra++)
{
t[ra].a = 10+ra;
for(int i = 0; i < 10; i++)
{
t[ra].b[i] = i+ra;
}
for(int i = 0; i <5; i++)
{
for(int j =0; j < 6; j++)
{
t[ra].c[i][j] = j+i+ra;
}
}
}
printf("{\n");
for(ra=0;ra<2;ra++)
{
printf("{/*%d*/\n",ra);
printf("/*a*/%d,\n",t[ra].a);
printf("{/*b*/");
for(int i = 0; i < 10; i++)
{
printf("%d,",t[ra].b[i]);
}
printf("},\n");
printf("{/*c*/\n");
for(int i = 0; i < 5; i++)
{
printf("{/*%d*/",i);
for(int j = 0; j < 6; j++)
{
printf("%d,",t[ra].c[i][j]);
}
printf("},\n");
}
printf("}/*c*/,\n");
printf("}/*%d*/,\n",ra);
}
printf("};\n");
#ifdef SECOND_PASS
printf("{\n");
for(ra=0;ra<2;ra++)
{
printf("{/*%d*/\n",ra);
printf("/*a*/%d,\n",t[ra].a);
printf("{/*b*/");
for(int i = 0; i < 10; i++)
{
printf("%d,",test[ra].b[i]);
}
printf("},\n");
printf("{/*c*/\n");
for(int i = 0; i < 5; i++)
{
printf("{/*%d*/",i);
for(int j = 0; j < 6; j++)
{
printf("%d,",test[ra].c[i][j]);
}
printf("},\n");
}
printf("}/*c*/,\n");
printf("}/*%d*/,\n",ra);
}
printf("};\n");
#endif
return(0);
}
这是C语言,所以你只需要自由地使用大括号,每个主要元素都有自己的一套,尽管gcc不喜欢a元素有自己的一套。
so.c:19:1: warning: braces around scalar initializer
{/*a*/10},
这也是练习的一部分,如果它抱怨的话,你就放宽心,然后退缩。
你可以看到我加了一个测试,看看如果第一部分的输出然后可以使用并编译干净。 然后对比第二个版本的两个输出,确认它们是一样的。 (在你的情况下,甚至我的情况下,输出会被拉到应用程序中,这就是你在编译时测试语法的地方,这只是这个答案的一个示范)。
而且正如指出的那样,你已经在做一个使用C来init,然后你可以使用C来制作C代码......。
你只是在声明前面加了const,让它成为一个const。由于这是const,你基本上希望所有的数据都被初始化,所以这种简单的方法就可以了。 如果你知道如果a=5,那么c就不用了,那么你可以使用其他答案中描述的一些快捷方式。 这些理想情况下会为未初始化的项目生成零。 或者你可以自己手动把零放进去,你并没有在rom中节省任何空间,所有的项目都是存在的,只是用这些快捷键节省了嵌入式应用源码的空间。
对于const这样的数据,我就很啰嗦了,经常用代码生成代码,并在代码中多加注释数据,以便于看清这些数据是什么,没必要为源码节省磁盘空间。
//---- 30 ----
0x00,0x07,0x07,0x07,0x07,0x07,0x00,0x00, //[ ##### ]
0x07,0x07,0x00,0x00,0x07,0x07,0x07,0x00, //[## ### ]
0x07,0x07,0x00,0x07,0x07,0x07,0x07,0x00, //[## #### ]
0x07,0x07,0x07,0x07,0x00,0x07,0x07,0x00, //[#### ## ]
0x07,0x07,0x07,0x00,0x00,0x07,0x07,0x00, //[### ## ]
0x07,0x07,0x07,0x00,0x00,0x07,0x07,0x00, //[### ## ]
0x00,0x07,0x07,0x07,0x07,0x07,0x00,0x00, //[ ##### ]
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //[ ]
//---- 31 ----
0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00, //[ ## ]
0x00,0x00,0x07,0x07,0x07,0x00,0x00,0x00, //[ ### ]
0x00,0x07,0x07,0x07,0x07,0x00,0x00,0x00, //[ #### ]
0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00, //[ ## ]
0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00, //[ ## ]
0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00, //[ ## ]
0x00,0x07,0x07,0x07,0x07,0x07,0x07,0x00, //[ ###### ]
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //[ ]
//---- 32 ----
0x00,0x07,0x07,0x07,0x07,0x07,0x00,0x00, //[ ##### ]
0x07,0x07,0x00,0x00,0x00,0x07,0x07,0x00, //[## ## ]
0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x00, //[ ## ]
0x00,0x00,0x00,0x07,0x07,0x07,0x00,0x00, //[ ### ]
0x00,0x07,0x07,0x07,0x00,0x00,0x00,0x00, //[ ### ]
0x07,0x07,0x00,0x00,0x00,0x07,0x07,0x00, //[## ## ]
0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x00, //[####### ]
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //[ ]
(是的,那个头然后压缩成每像素一个位的头,以节约rom空间,这要看我怎么用,在哪里用。代码生成代码,然后更多的代码生成更多的代码,然后用这个代码)。
当然你可以手动生成初始化数据不用编程。
需要注意的是
/*a*/
/*0*/
东西只是为了让你更容易掌握事情的进展。缩进确实也有助于看清事情的发展,你也可以这样做。
const testStruct test[2]=
{
{
10,
{0,1,2,3,4,5,6,7,8,9,},
{
{0,1,2,3,4,5,},
{1,2,3,4,5,6,},
{2,3,4,5,6,7,},
{3,4,5,6,7,8,},
{4,5,6,7,8,9,},
},
},
{
11,
{1,2,3,4,5,6,7,8,9,10,},
{
{1,2,3,4,5,6,},
{2,3,4,5,6,7,},
{3,4,5,6,7,8,},
{4,5,6,7,8,9,},
{5,6,7,8,9,10,},
},
},
};
或者两者结合。
注意,有些编译器不喜欢这些额外的逗号,所以你可以很容易地把这些逗号去掉。
这个
{3,4,5,6,7,8},
而不是
{3,4,5,6,7,8,},
这个
}/*c*/,
}/*1*/
};
而不是这样
}/*c*/,
}/*1*/,
};
只要你有硬盘空间,你就可以把它做得很大,当然你的目标和一些工具链工具或编辑器不能处理无限大的文件。 所以你会有一些限制。 在这些限制范围内,它都会有一定的规模。
如果你知道怎么做。
const int x = 5;
const float y = 1.5F;
const double z = 2.1;
const char s[]="hello world";
const int a[5]={1,2,3,4,5};
那么你基本上已经知道答案了;只要自由地使用{大括号}。
我主要是把其他答案可视化而已)。