C文本常量:在头文件还是C文件中?

C文本常量:在头文件还是C文件中?,c,header,constants,C,Header,Constants,我想在一个单一的静态C程序中包含一组数据(比如,图像,还有其他数据,嵌入到可执行文件中,因为我在一个没有文件的嵌入式平台上工作) 因此,我编写了一个小img2c,从我的数据文件中创建常量数据,创建一个带有静态常量数组初始值设定项的文件,并将其放入闪存(使用C99 nice特性) 我的问题是,我应该把它们放在一个.h文件中,就像我见过很多次的那样——举个例子,gimp可以另存为.h文件,而不是.c文件——或者放在一个.c文件中,在头文件中引用,只使用const-extern声明以供进一步引用,而不

我想在一个单一的静态C程序中包含一组数据(比如,图像,还有其他数据,嵌入到可执行文件中,因为我在一个没有文件的嵌入式平台上工作)

因此,我编写了一个小img2c,从我的数据文件中创建常量数据,创建一个带有静态常量数组初始值设定项的文件,并将其放入闪存(使用C99 nice特性)

我的问题是,我应该把它们放在一个.h文件中,就像我见过很多次的那样——举个例子,gimp可以另存为.h文件,而不是.c文件——或者放在一个.c文件中,在头文件中引用,只使用const-extern声明以供进一步引用,而不必包含所有数据并将其全部传递给编译器,并且每次使用时都重新声明它


预处理器宏是不可能的,因为我将引用它们的地址,而不是每次都包含整个数据。

如果将数据放在头中,则拉入该头的每个编译单元都将获得自己的数据副本。设想两个.c文件,每个文件都指向一个.o。每个.o都有一个数据副本,您的最终可执行文件可能比需要的大

如果你把它放在一个.c文件中,并把它放在一个头文件中,那么只有一个.o文件会包含数据,你的最终可执行文件会更小。此外,如果您更改了一些内容,那么如果只是更改单个.c文件而不是所有包含头文件的.c文件,那么重新编译会更快


正如您所指出的,链接器也可能会出现问题,因为符号将被定义多次,请参阅的答案。在页眉中放一个extern,在a.c中放一个数据会更好,就像在c中的所有内容一样,这里有一些关于最佳实践的争论。通常的做法是将实际值放在实现(.c)中,将声明(
extern something
)放在头(.h)中。这样,您就可以更新这些值,而无需重新编译包含标头的每个文件


答案几乎永远不会是“每次使用时都重新声明它。”

这可以通过确保变量仅在单个源文件中定义来实现。为此,需要一点预处理器“编程”

头文件:

/* Standard include guard */
#ifndef X_H
#define X_H

#ifdef X_SOURCE
uint8_t data[] = { /* ... */ };
#else
extern uint8_t data[];
#endif

#endif  /* End of include guard */
源文件:

#define X_SOURCE
#include "x.h"

/* ... */

所有其他源文件只需要包含文件
“x.h”
,它们可以引用
数据

头文件,在C中没有什么特别之处;
.h
扩展名不会改变编译器处理它们的方式。这更像是对人类的一个提示,“这个文件可能不包含任何代码”

因此,如果您将实际的二进制数据放入其中,编译器将在包含头的每个文件中创建数组的副本(而不是简单地添加对共享全局数组的引用)


GIMP创建一个头文件,因为它不知道您计划如何使用数据。这样做的想法是,您将在
.c
文件中只包含此头文件一次,然后该文件将以某种方式处理数据。如果它编写了一个
.c
文件,而您对代码进行了更改,那么当您要求GIMP更新数据时,GIMP将不得不合并这些更改-这将是混乱的。

作为一个备用GIMP,GIMP可以保存到c源文件以及头文件中。在文件->另存为下,将文件类型更改为C源代码。当然可以。我的意见是,默认情况下,它保存到.h,这似乎违反直觉。虽然这样做有效,但有一个缺点:如果数组很大,至少预处理器必须对其进行解析->这将减慢编译器的速度。@Aarondigula,我可以向你保证,现在预处理器的性能几乎从来都不是问题。这里是
#if
条件中的代码。在查看
#
的第一个非空字符时,这样的行可以(而且是!)跳过。链接步骤不会删除副本吗?@Eregrith,不,不是标准的C功能。你必须把它们做成“弱”的符号。