如何解决如何在 C 头文件中构建复杂数组?
我正在尝试使用初始化程序在 C 头文件中构建一个数组。这也可以使用可执行代码在函数中完成,但我认为标头将是一个更优雅的解决方案。我对数据结构不是很了解,并且在搜索网络时很难找到解决方案。我的骨架看起来像这样:
typedef struct fontTypeDef {
uint16_t xOffset;
uint16_t yOffset;
uint16_t width;
uint16_t height;
uint32_t* bitmapPtr;
} fontTypeDef;
fontTypeDef font[128];
font[32] = {0,8,1 {0xDEADBEEF,0xDEADBEEF,0x0}};
font[33] = {1,2,3,1 {0xABCDEF,0x0,0xFF,0xAB,0xCC,0x12321}};
.
.
.
这是如何正确完成并提高内存效率的?
编辑 1: 很多很好的反馈,暗示这不能仅在头文件中完成。我还应该提供更多背景信息。我编写了一个 Python 脚本,可以将给定的 true-type 字体转换为头文件(当然,我也可以写入 .c 文件或其任意组合)。目的是在 STM32 µC 中使用位位传输,并在将文本写入 LCD 时利用 DMA 2D。我看到示例代码是通过利用如下所示的头文件中的定义来简单地对图片进行位点传输:
const uint32_t RGB565_480x272[65280] =
{
0x7A537A53,0x82538253,0x8A538253,.
.
}
编辑 2:这是@Eric Postpischil 概述的解决方案。
在头文件中:
typedef struct fontTypeDef {
uint16_t xOffset;
uint16_t yOffset;
uint16_t width;
uint16_t height;
const uint32_t *bitmapPtr;
} fontTypeDef;
extern const fontTypeDef font[128];
在 C 文件中:
const fontTypeDef font[128] =
{
…
[32] = {0,1,(uint32_t []) {0xDEADBEEF,0x0}},[33] = {1,(uint32_t []) {0xABCDEF,0x12321}},…
}
解决方法
切勿在头文件中定义数据和函数(static inline
除外)。在头文件中只放置类型声明、函数原型和 extern
对象声明。
标题应该只包含 font
数组的类型声明和外部声明(尽管使全局声明肯定不是“优雅”):
#if !defined FONT_H_INCL
#define FONT_H_INCL
typedef struct fontTypeDef {
uint16_t xOffset;
uint16_t yOffset;
uint16_t width;
uint16_t height;
uint32_t bitmap[8];
} fontTypeDef;
extern fontTypeDef font[128];
#endif
然后在说 font.c 你有:
fontTypeDef font[128] = { {0,8,1,{0xDEADBEEF,0xDEADBEEF,0x0},// 0
{1,2,3,{0xABCDEF,0x0,0xFF,0xAB,0xCC,0x12321} // 1
...
{0,0x0} // 126
{1,0x12321} }; // 127
然后您include font.h,在其中引用font
或fontTypeDef
分别编译和链接 font.c.
综上所述,如果您的目标是“优雅”,那么您真的不应该这样做。以这种方式使用全球数据是一种糟糕的做法,也是不必要的。而是在 .c 中将 font
设为静态并提供访问功能。
...但我认为标题会是一个更优雅的解决方案
我不知道是什么让你这么认为,但绝对是错误的。
永远不要将 fontTypeDef font[128];
之类的变量定义放入头文件 - 永远不要。
原因很简单。在具有多个源文件(即 c 文件)的项目中,您需要能够在所有这些源文件中包含您的头文件。如果头文件包含变量定义,那将是不可能的,因为在这种情况下,每个源文件都定义了该变量,并且最终会得到多个具有相同名称的变量。这是不允许的。
正确的方法是在源文件中定义您的变量,并在头文件中放置一个 extern 声明。喜欢
标题
extern fontTypeDef font[128];
来源
fontTypeDef font[128];
现在您可以在任意数量的源文件中包含标题。
但是……即使那样也应该避免。一般来说,拥有全局范围的变量是一个坏主意。尽可能缩小变量的范围。充其量将它们保留在函数中。如果这不可行,至少将范围限制为当前源文件:
static fontTypeDef font[128];
作为最后的评论:
类似的东西
font[32] = {0,1 {0xDEADBEEF,0x0}};
不能放在函数外。
如果你想在函数外为 font
赋值,你必须使用如下的初始化器:
static fontTypeDef font[128] = { ... };
此外,最后一个结构体成员是一个指针,所以你不能使用像这样的初始化器:
{0,0x0}}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This can't initialize a pointer
不过你可以这样做
static fontTypeDef font[128] = {
{0,NULL},{0,4,6,NULL}
};
然后在稍后的某个函数中设置指针值。
,把它放在头文件中就可以达到你想要的效果:
typedef struct fontTypeDef {
uint16_t xOffset;
uint16_t yOffset;
uint16_t width;
uint16_t height;
const uint32_t *bitmapPtr;
} fontTypeDef;
extern const fontTypeDef font[128];
这在一个源文件中:
const fontTypeDef font[128] =
{
…
[32] = {0,(const uint32_t []) {0xDEADBEEF,0x0}},[33] = {1,(const uint32_t []) {0xABCDEF,0x12321}},…
}
注意事项:
- 这会将声明放在头文件中,使它们对包含该头文件的所有源文件可见。它将数据的定义放在一个源文件中,这样数组
font
在整个程序中只会被定义一次。 - 尽管其他评论和答案警告不要使用“全局”变量或文件范围标识符,但以这种方式定义常量数据是合理的。只要您希望将数据永久内置到程序中,而不是从参考文件中读取,就可以了。
-
const
已添加,因为我们正在定义在程序构建时(或之前)固定的数据,并且不应由程序更改。 - 指针成员使用复合文字初始化。语法
(type) { initial values… }
创建一个初始化对象。上面的代码使用它来创建一个uint32_t
数组。该数组会自动转换为指向其第一个元素的指针,并且该指针会初始化bitmapPtr
元素。 -
font
数组的元素使用指定的初始化语法[index] = { initial values… }
进行初始化,其中 index 给出正在初始化的数组元素的索引。如果按顺序初始化所有数组元素,也可以简单地使用{ initial values… }
的列表,省略[index] =
。 - 原始代码中初始值后面的分号已更改为逗号,因为它现在是数组定义中的初始值设定项列表,而不是语句序列。
如果你创建了一个变量并且你想在你的源代码中使用它,你必须让编译器知道你正在使用一个在头文件中通过关键字 extern
声明的变量,所以你使用的典型头文件声明源变量看起来像这样
//header.h
int a = 5;
//source.c
extern int a;
这告诉编译器不要为 a
生成变量,而是在头文件中查找它。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。