2

Is it OK do do something like this?

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

I am changing the constant struct member by writing to the entire struct from a file. How is that supposed to be handled? Is this undefined behavior, to write to a non-constant struct, if one or more of the struct members is constant? If so, what is the accepted practice to handle constant struct members?

Jens
  • 69,818
  • 15
  • 125
  • 179

3 Answers3

3

It's undefined behavior.

The C11 draft n1570 says:

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.

My interpretation of this is: To be compliant with the standard, you are only allowed to set the value of the const member during object creation (aka initialization) like:

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

Doing

AStruct.y = 'b';   // Error

should give a compiler error.

You can trick the compiler with code like:

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

It will probably work fine on most systems but it's still undefined behavior according to the C11 standard.

Also see memcpy with destination pointer to const data

Community
  • 1
  • 1
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • If `AStruct` is an object of automatic duration, its lifetime will start before the initialization is performed, and thus the initialization would change the value of the object within its lifetime. On the other hand, the Standard allows implementations to usefully define constructs in cases beyond those mandated by the Standard, and the authors saw no need to list all the cases where doing otherwise would have been recognized as obtuse and useless. – supercat Jun 23 '20 at 16:59
  • @supercat Hmm... maybe... but where in the standard do you see that the lifetime of an object starts before the initialization? My interpretation is that the lifetime starts once it's initialized. – Support Ukraine Jun 23 '20 at 18:28
  • N1570 6.2.4 paragraph 6: "For such an object [one of automatic storage duration] that does not have a variable length array type, its lifetime extends *from entry into the block with which it is associated* until execution of that block ends in any way." If computation of any value in an initialization had an observable side effects, such side effects could not occur until execution entered the block, implying that the lifetime of the object would have begun. – supercat Jun 23 '20 at 18:46
  • Note that one may do something like `struct foo {struct foo *next, ...whatever...} myFoo = {&myFoo};`, implying that `foo` receives an address before initialization is complete. – supercat Jun 23 '20 at 18:49
  • @supercat I think this is a corner case left for interpretation. My interpretetion is that the lifetime of an object starts once the initialization is complete. – Support Ukraine Jun 23 '20 at 19:09
  • In what way is the Standard's wording unclear? The authors of the Standard expressly acknowledged in the published Rationale the possibility of an implementation that is "conforming" but "succeeds at being useless". Thus, I don't think they thought it necessary to worry about whether a constraint allowed for an obviously-useful corner case if all existing implementations would process that case meaningfully anyhow and there was no imaginable reason that anyone seeking to write a quality implementation might plausibly do otherwise. – supercat Jun 23 '20 at 19:35
2

How are constant struct members handled in C?

Read the C11 standard n1570 and its §6.7.3 related to the const qualifier.

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

It depends if you care more about strict conformance to the C standard, or about practical implementations. See 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 of your software (to other platforms).

It is likely that you won't spend the same efforts on the embedded software of a 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.

Consider also using static analysis tools such as Frama-C or the Clang static analyzer on your code.

Regarding undefined behavior, be sure to read this blog.

See also this answer to a related question.

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

Then 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, could be inspirational).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

The Standard is a bit sloppy in its definition and use of the term "object". For a statement like "All X must be Y" or "No X may be Z" to be meaningful, the definition of X must have criteria that are not only satisfied by all X, but that would unambiguously exclude all objects that aren't required to be Y or are allowed to be Z.

The definition of "object", however, is simply "region of data storage in the execution environment, the contents of which can represent values". Such a definition, however, fails to make clear whether every possible range of consecutive addresses is always an "object", or when various possible ranges of addresses are subject to the constraints that apply to "objects" and when they are not.

In order for the Standard to unambiguously classify a corner case as defined or undefined, the Committee would have to reach a consensus as to whether it should be defined or undefined. If the Committee members fundamentally disagree about whether some cases should be defined or undefined, the only way to pass a rule by consensus will be if the rule is written ambiguously in a way that allows people with contradictory views about what should be defined to each think the rule supports their viewpoint. While I don't think the Committee members explicitly wanted to make their rules ambiguous, I don't think the Committee could have been consensus for rules that weren't.

Given that situation, many actions, including updating structures that have constant members, most likely falls in the realm of actions which the Standard doesn't require implementations to process meaningfully, but which the authors of the Standard would have expected that implementations would process meaningfully anyhow.

supercat
  • 77,689
  • 9
  • 166
  • 211