c - 如何初始化常量结构

Hoo*_*ood 0 c struct global header constants

我想避免在我的 C 文件中硬编码常量值,所以我想知道是否有办法直接在头文件中初始化结构常量,以便在我包含头文件的任何地方使用它?(以 #define 适用于简单类型常量的方式)

到目前为止我找到的所有答案都是:

const int var = 5; /*in header*/ 
Run Code Online (Sandbox Code Playgroud)

只适用于 C++(不适用于 C)

使用 C 文件来初始化常量,这不是我要找的

这篇文章的最佳答案:如何使用 extern 在源文件之间共享变量?

这似乎有点复杂......

预先感谢您能给我带来的答案或帮助!:)

编辑:我正在为我的问题添加更多详细信息。

我想存储我在结构中使用的系统的硬件参数:

struct parameters_Component1 {
     int const1 = 5;
     double const2 = 7,847
}
struct parameters_Component2 {
     int const1 = 6;
     double const2 = 9,3343
}
Run Code Online (Sandbox Code Playgroud)

或等效于的结构

#define myConst 5;
Run Code Online (Sandbox Code Playgroud)

我希望将这些常量值重新分组到一个我可以访问和修改的头文件中,而不是出于组织目的而放在我的 C 代码中

Joh*_*ger 7

我想避免在我的 C 文件中硬编码常量值,所以我想知道是否有办法直接在头文件中初始化结构常量,以便在我包含头文件的任何地方使用它?(以 #define 适用于简单类型常量的方式)

您首先需要更清楚地了解相对于 C 语言语义的含义。在 C 术语中,常量是表示源代码中特定值的语法结构。它们没有自己的关联存储,并且仅适用于内置类型。你想要的不是这个意义上的“常量”,或者至少,C 没有提供这个意义上的结构常量。这与类型限定符无关const

C 在这个一般领域提供了一些东西:

  • 结构初始化器,其工作方式与初始化结构类型对象的常量类似;
  • 具有不可修改的( ) 结构类型的对象const;和
  • 结构类型的复合文字。

初始化器

顾名思义,初始化器可以在对象声明中使用来初始化声明的对象。然而,它们本身不是值,因此不能在声明后将它们分配给对象,也不能在需要表达式的情况下使用它们。您可以定义一个扩展为初始值设定项的宏,有时会这样做。例子

header1.h

struct my_struct { int x; int y; };
#define MY_STRUCT_INITIALIZER { .x = 0, .y = 0 }
Run Code Online (Sandbox Code Playgroud)

代码1.c

// ...
// This is initialization, not assignment:
struct my_struct s = MY_STRUCT_INITIALIZER;
Run Code Online (Sandbox Code Playgroud)

初始化器没有自己的存储空间。

不可修改的对象

与 C++ 中一样,任何数据类型都可以通过const- 限定来生成无法修改的对象类型。因此,此类对象必须从初始化器获取它们的值,或者是函数参数,或者以导致它们被默认初始化的方式声明,因为不可修改意味着没有其他方法来定义它们的值。与初始值设定项不同,这些是具有数据类型和关联存储的真正对象,并且它们可以在与其类型兼容的任何表达式中使用。类似地,与对象关联的标识符const必须满足 C 的范围和链接规则,就像其他标识符一样。特别是,尽管任何具有外部(或内部)链接的对象都可以有多个声明,但每个对象只能有一个定义

外部对象

如果您想“在任何地方”使用相同的对象,那么这意味着外部链接。好的样式要求在标头中声明该对象,但不能在标头中定义它,因为如果标头包含在多个翻译单元中,则会导致重复定义。C 通过以下习惯用法很好地支持了这一点:

header2.h

struct my_struct { int x; int y; };

// a declaration:
extern const struct my_struct shared_struct;  // NOTE: explicit "extern" and no initializer
Run Code Online (Sandbox Code Playgroud)

my_struct_stuff.c

#include "header2.h"

// a definition:
const struct my_struct shared_struct = { .x = 1, .y = 2 };
Run Code Online (Sandbox Code Playgroud)

其他.c

#include "header2.h"

// no definition of shared_struct here

// ...
int x = shared_struct.x;
int y = shared_struct.y;
// ...
Run Code Online (Sandbox Code Playgroud)

这提供了一个不可修改的对象,可以在任意数量的翻译单元之间共享,但它不满足将所有内容都保留在标头中的标准。可以玩条件编译游戏,让定义按词法出现在标头中,但您仍然需要一个指定的源文件来提供定义。(细节留作练习。)

内部对象

或者,如果在每个翻译单元中使用等效但不同的对象就足够了,那么您的标头可以定义一个不可修改的对象,其标识符具有内部链接。然后,包含标头的每个翻译单元都将拥有自己的独立对象副本:

header3.h

struct my_struct { int x; int y; };
static const struct my_struct local_struct = { .x = 1, .y = 2 };
Run Code Online (Sandbox Code Playgroud)

这满足了将所有内容保留在头文件中的标准,但即使不考虑存储使用的考虑,您也可能有理由希望为每个翻译单元提供相同的对象,但这并不能实现该目标。此外,您的编译器可能会针对包含标头但不访问local_struct.

复合文字

C 还具有结构类型的复合文字,它们在某些方面类似于字符串文字。它们代表真正的对象,具有数据类型和存储,并且具有直接传达对象值的词汇形式。复合文字可能具有const- 限定类型,但默认情况下并非如此const,并且一般来说,以其类型允许的任何方式修改与复合文字对应的对象是可以接受的。此外,与字符串文字不同,复合文字不一定具有静态存储持续时间。此外,复合文字的每次出现都代表一个单独的对象。

从词法上来说,复合文字类似于结构体、联合或数组类型的强制转换运算符,应用于该类型对象的相应初始值设定项:

header4.h

struct my_struct { int x; int y; };

#define MY_STRUCT_LITERAL ((struct my_struct) { .x = 42, .y = 42 })
/* or:
#define MY_STRUCT_LITERAL ((const struct my_struct) { .x = 42, .y = 42 })
 */
Run Code Online (Sandbox Code Playgroud)

扩展为复合文字的宏可以在标头中定义,如图所示,但重要的是要了解此类宏的每次出现都将对应于一个单独的对象。在某些情况下,编译器可能会优化保留任何实际存储,但复合文字并不是与整数或浮点常量相同意义上的“常量”。

全面的

上面列出了您的主要可用替代方案。我不清楚您对将值按词法出现在头文件中(而不是在表示翻译单元的随附专用源文件中)有何重视或有多大价值。我也不清楚您认为最小化这些数据的存储需求的相对重要性是什么,或者结构的对象标识是否需要很重要。由于您提出了 C++ 进行比较,我不应该忽视这样一个事实:复合文字是 C++ 不提供的 C 功能。这些是您在选择这些可能性时应该牢记的考虑因素。

或者您也可以考虑是否真的想将数据排列在实际的结构对象中。我之所以关注这一点,是因为这是您所问的问题,但是有一些基于宏的选项,可以让您以相当紧凑的形式在标题中列出数据,并使用类似函数的宏而不是结构访问语法来访问他们。