我在头文件中有一个这样的结构。
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 中的常量时简单地执行循环。您应该找到解决此问题的另一种方法。我可以建议两个解决方案:
- 使用在线数组生成器,如下所示:https://yupana-engineering.com/online-c-array-generator,或
- 使用BASH,Python或其他脚本语言编写自己的生成器,这些脚本语言可以在预构建阶段集成到构建过程中。
这通常是通过"硬编码"所有内容来完成的。如果初始值设定项很复杂,则初始值设定项的 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
您已经以正常/典型/简单的方法执行此操作?
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 来初始化,然后你可以使用 C 来制作 C 代码......
您只需将 const 放在声明的前面,使其成为一个 const。由于这是常量,您基本上希望/需要初始化所有数据,因此这种简单的方法将起作用。 如果您知道如果 a = 5 则不使用 c,那么您可以使用其他答案/注释中描述的一些快捷方式。 理想情况下,这些将为未初始化的项目生成零。 或者你可以自己手动把零放进去,你不会在所有项目都存在的rom中节省任何空间,只是使用这些快捷方式节省嵌入式应用程序源代码中的空间。
对于像这样的常量数据,我变得非常冗长,经常使用代码来生成代码,并在代码中放置额外的注释数据以使其更容易查看该数据是什么,无需为源代码节省磁盘空间。
//---- 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};
那么你基本上已经知道答案了;只要用{花括号}保持自由。
(我主要只是想象其他答案/评论)。