【问题标题】:c - How to initialize a constant structurec - 如何初始化一个常量结构
【发布时间】:2020-02-24 12:01:48
【问题描述】:

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

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

const int var = 5; /*in header*/ 

仅适用于 C++(而非 C)

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

本帖最佳答案:How do I use extern to share variables between source files?

好像有点复杂……

提前感谢您给我的答案或帮助! :)

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

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

struct parameters_Component1 {
     int const1 = 5;
     double const2 = 7,847
}
struct parameters_Component2 {
     int const1 = 6;
     double const2 = 9,3343
}

或等效于

的结构
#define myConst 5;

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

【问题讨论】:

  • 你能展示一些你想要完成的示例代码吗?目前尚不清楚您是否正在尝试定义可用作初始化程序的东西或什么。
  • edit您的问题包括minimal reproducible example,以便我们知道您要做什么。
  • 编辑问题,然后投票重新打开。
  • static struct qux { int foo, bar; } const s1 = { 1, 2 }; 定义了struct qux 类型的常量变量s1。您可以将它包含在您拥有的所有翻译单元中(但当然不能相乘)并使用常量s1 例如初始化struct qux 类型的其他变量。你是这个意思吗?
  • const int var = 5; 如果您将头文件包含在多个 .c 文件中,则会失败。不要在头文件中声明变量。

标签: c struct header constants global


【解决方案1】:

我想避免在我的 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 }

code1.c

// ...
// This is initialization, not assignment:
struct my_struct s = MY_STRUCT_INITIALIZER;

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

不可修改的对象

与在 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

my_struct_stuff.c

#include "header2.h"

// a definition:
const struct my_struct shared_struct = { .x = 1, .y = 2 };

other.c

#include "header2.h"

// no definition of shared_struct here

// ...
int x = shared_struct.x;
int y = shared_struct.y;
// ...

这提供了一个不可修改的对象,在您喜欢的多个翻译单元之间共享,但它不满足您将所有内容都保存在标题中的标准。可以玩条件编译游戏以使定义以词汇方式出现在标题中,但您仍然需要一个指定的源文件来提供定义。 (细节留作练习。)

内部对象

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

header3.h

struct my_struct { int x; int y; };
static const struct my_struct local_struct = { .x = 1, .y = 2 };

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

复合文字

C 还具有结构类型的复合文字,在某些方面类似于字符串文字。它们代表真正的对象,具有数据类型和存储,并具有直接传达对象值的词汇形式。复合文字可能有const-qualified 类型,但默认情况下它们不是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 })
 */

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

总体

上面介绍了您的主要可用替代方案。我不清楚你对让这些值在头文件中以词法方式出现,而不是在代表翻译单元的随附的专用源文件中,给予什么或多少价值。我也不清楚您认为最小化这些数据的存储需求的相对重要性,或者结构的对象身份是否需要重要。并且由于您在比较中提出了 C++,我不应该忽视复合文字是 C++ 不提供的 C 特性这一事实。这些是您在选择这些可能性时应牢记的注意事项。

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

【讨论】:

  • 感谢非常好的解释!我在 simulink 中使用这些常量。从我的 simulink 模型生成 C 代码时,我告诉 simulink 这些常量是在外部 h 文件中定义的,它可以在生成时包含在其 C 文件中。因为我只将生成的代码链接到头文件,所以我希望在该头文件中定义所有内容。确实,您的解决方案同时使用 .h 和 .C 似乎是最好的答案。但是现在我需要找到一种方法来告诉代码生成工具也去寻找一个 .c 文件,我不知道该怎么做(还;))无论如何,谢谢!!
  • aaaaa 我想通了!感谢您的帮助!
  • C23 将添加对包括“maybe_unused”在内的属性的支持。它可用于便携地使编译器对未使用的“静态常量”保持沉默
猜你喜欢
  • 2018-10-18
  • 2012-08-18
  • 1970-01-01
  • 2014-05-25
  • 1970-01-01
  • 2013-11-21
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多