如何在 C 中处理常量结构成员?

How are constant struct members handled in C?

做这样的事情可以吗?

struct MyStruct {
    int x;
    const char y; // notice the const
    unsigned short z;
};
struct MyStruct AStruct;
fread(&MyStruct, sizeof (MyStruct), 1,
      SomeFileThatWasDefinedEarlierButIsntIncludedInThisCodeSnippet);

我正在通过从文件写入整个结构来更改常量结构成员。这应该如何处理?如果一个或多个结构成员是常量,这是写入非常量结构的未定义行为吗?如果是这样,处理常量结构成员的公认做法是什么?

How are constant struct members handled in C?

阅读 C11 标准 n1570 及其与 const 限定符相关的 §6.7.3。

If so, what is the accepted practice to handle constant struct members?

这取决于您更关心严格遵守 C 标准,还是更关心实际实现。请参阅 this draft report (work in progress in June 2020) discussing these concerns. Such considerations depend on the development efforts allocated on your project, and on portability 您的软件(到其他平台)。

您很可能不会在 Covid respirator (or inside some ICBM) and on the web server (like lighttpd or a library such as libonion or some FastCGI application) inside a cheap consumer appliance or running on some cheap rented Linux VPS 的嵌入式软件上花费同样的精力。

还可以考虑在您的代码中使用静态分析工具,例如 Frama-C or the Clang static analyzer

关于未定义的行为,请务必阅读this blog

另请参阅 this 对相关问题的回答。

I am changing the constant struct member by writing to the entire struct from a file.

然后endianness issues and file system issues are important. Consider perhaps using libraries related to JSON, to YAML, perhaps mixed to sqlite or PostGreSQL or TokyoCabinet (and the source code of all these open source libraries, or from the Linux kernel,可能会鼓舞人心。

这是未定义的行为。

C11 草案 n1570 说:

6.7.3 Type qualifiers

...

...

If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.

我对此的解释是:为了符合标准,您只能在对象创建(又名初始化)期间设置 const 成员的值,例如:

struct MyStruct AStruct = {1, 'a', 2};  // Fine

正在做

AStruct.y = 'b';   // Error

应该给出编译器错误。

您可以使用如下代码欺骗编译器:

memcpy(&AStruct, &AnotherStruct, sizeof AStruct);

它可能在大多数系统上都能正常工作,但根据 C11 标准,它仍然是未定义的行为。

另见 memcpy with destination pointer to const data

标准在定义和使用术语“对象”时有点草率。对于像“所有 X 必须是 Y”或“没有 X 可能是 Z”这样的陈述是有意义的,X 的定义必须具有不仅所有 X 都满足的标准,而且会明确排除所有不满足的对象必须为Y或允许为Z。

然而,“对象”的定义只是“执行环境中的数据存储区域,其内容可以表示值”。然而,这样的定义未能明确是否每个可能的连续地址范围始终是一个“对象”,或者各种可能的地址范围何时受到适用于“对象”的约束以及何时不是。

为了让标准明确地将极端情况分类为已定义或未定义,委员会必须就它是应该定义还是未定义达成共识。如果委员会成员根本不同意某些情况是否应该定义或未定义,则以协商一致方式通过规则的唯一方法是规则的编写方式含糊不清,允许人们对每个人认为应该定义什么有矛盾的看法该规则支持他们的观点。虽然我不认为委员会成员明确想让他们的规则变得模棱两可,但我认为委员会不可能就不是的规则达成共识。

鉴于这种情况,许多操作(包括更新具有常量成员的结构)很可能属于标准不要求实现进行有意义处理但标准作者期望的操作领域无论如何,实现都会有意义地处理。