0

I would like to avoid having constant values hardcoded in my C files, so I was wondering if i had a way to intialize a struct constant directly in a header file to use it everywhere i included the header file? (in the way #define works for constants with simple types)

All answers i found so far have been:

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

which only works in C++ (not C)

Using a C file to initialize the constant which is not what i'm looking for

The best answer to this post: How do I use extern to share variables between source files?

which seems a bit complicated...

Thanks in advance for the answers or help you can bring me! :)

EDIT : I am adding more details to my question.

I want to stock hardware parameters of the system i use in a structure :

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

or a struct equivalent to

#define myConst 5;

I want to have those constants values regrouped in a header file i can access and modify rather than in my C code for organisation purpose

Hood
  • 63
  • 1
  • 7
  • 1
    Can you show some example code of what you're trying to accomplish? It's not clear if you're trying to define something that can be used as an initializer or what. – Jonathon Reinhart Feb 24 '20 at 12:05
  • What do you mean "struct constant"? A struct which is constant, or with a const member, or one which is initialized to a const value? – Lundin Feb 24 '20 at 12:05
  • What has the linked question and answer to do with structures? – RobertS supports Monica Cellio Feb 24 '20 at 12:08
  • 1
    Please [edit] your question to include [mcve], so we know what you are trying to do. – user694733 Feb 24 '20 at 12:09
  • 1
    Edit the question, then vote to reopen. – RobertS supports Monica Cellio Feb 24 '20 at 12:16
  • 3
    `static struct qux { int foo, bar; } const s1 = { 1, 2 };` defines a constant variable `s1` of type `struct qux`. You can include it in all translation units you have (but not multiply of course) and use the constant `s1` for example to initialize other variables of type `struct qux`. Is that what you meant? – Ctx Feb 24 '20 at 12:21
  • 1
    `const int var = 5;` in a header file will fail if you include the header file in more then one .c file. Don't declare variables in header files. – Jabberwocky Feb 24 '20 at 12:25
  • @RobertSsupportsMonicaCellio in the linked question, the first answer explains a way to intialize global variables using macros in the header file, which seems like a way to set my constants even through it seems complicated... I edited with more information on what i'm looking for but I cannot vote on my own post to re-open it... – Hood Feb 24 '20 at 13:06
  • @Hood I did that for you. – RobertS supports Monica Cellio Feb 24 '20 at 13:06

1 Answers1

6

I would like to avoid having constant values hardcoded in my C files, so I was wondering if i had a way to intialize a struct constant directly in a header file to use it everywhere i included the header file? (in the way #define works for constants with simple types)

You first need to get a clearer idea of what you mean, relative to the semantics of the C language. In C terminology, constants are syntactic constructs that represent specific values in source code. They do not have their own associated storage, and they are available only for built-in types. What you want is not a "constant" in this sense, or at least, C does not provide for structure constants in that sense. This has nothing whatever to do with the const type qualifier.

There are several things that C does offer in this general area:

  • structure initializers, which work similarly to constants for initializing objects of structure types;
  • objects having unmodifiable (const) structure type; and
  • compound literals of structure type.

Initializers

As their name suggests, initializers can be used in object declarations to initialize the declared objects. They are not values per se, however, so they cannot be assigned to objects post-declaration or otherwise used where an expression is required. You can define a macro that expands to an initializer, and this is sometimes done. Example

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;

An initializer has no storage of its own.

Unmodifiable objects

As in C++, any data type can be const-qualified to produce a type for objects that cannot be modified. Such objects thus must take their value from an initializer, or be function parameters, or else be declared in a way that causes them to be default-initialized, for being unmodifiable means there is no other way to define their values. Unlike an initializer, these are bona fide objects with data types and associated storage, and they can be used in any expression compatible with their type. Similarly, the identifiers associated with const objects must satisfy C's rules for scope and linkage, just like other identifiers. In particular, although there can be multiple declarations of any object with external (or internal) linkage, there can be only one definition of each.

External objects

If you want to use the same object "everywhere", then that implies external linkage. Good style then calls for that object to be declared in a header, but it cannot be defined in a header because that would result in duplicate definitions if the header were included in more than one translation unit. This is well supported by C via the following idiom:

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;
// ...

This affords a single unmodifiable object, shared among as many translation units as you like, but it does not satisfy your criterion of keeping everything in a header. It is possible to play conditional compilation games to get the definition to appear lexically in the header, but you still need exactly one designated source file that makes provision for providing the definition. (Details left as an exercise.)

Internal objects

Alternatively, if it is sufficient to use an equivalent but different object in each translation unit, then your header can define an unmodifiable object whose identifier has internal linkage. Each translation unit that includes the header will then have its own, independent copy of the object:

header3.h

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

This satisfies your criterion of keeping everything in the header file, but you may have reason, even apart from storage-use considerations, for wanting to provide the same object to each translation unit, and this does not achieve that objective. Also, your compiler may emit warnings for translation units that include the header but do not access local_struct.

Compound literals

C also has compound literals of structure types, which are analogous in some ways to string literals. They represent bona fide objects, with data types and storage, and have a lexical form that conveys the objects' values directly. Compound literals may have const-qualified types, but they are not const by default, and in general, it is acceptable to modify an object corresponding to a compound literal in any way permitted by its type. Also, unlike string literals, compound literals do not necessarily have static storage duration. Furthermore, each appearance of a compound literal represents a separate object.

Lexically, a compound literal resembles a cast operator for a structure, union, or array type, applied to a corresponding initializer for an object of that type:

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 })
 */

Macros expanding to compound literals can be defined in headers, as shown, but it is important to understand that each appearance of such a macro will correspond to a separate object. Under some circumstances, the compiler might optimize out reserving any actual storage, but a compound literal is not a "constant" in the same sense as an integer or floating constant.

Overall

Your main available alternatives are presented above. It's unclear to me what or how much value you place on having the values appear lexically in a header file, as opposed to, say, in an accompanying for-purpose source file representing a translation unit. It's also unclear to me what relative importance you attribute to minimizing storage requirements for these data, or whether the object identities of the structures need to be significant. And inasmuch as you raised C++ in comparison, I should not overlook the fact that compound literals are a C feature that C++ does not provide. These are the considerations you should bear in mind in choosing among those possibilities.

Or you could also consider whether you really want to arrange the data in actual structure objects at all. I have focused on that because it's what you asked about, but there are macro-based options that would allow you to tabulate your data in a header, in fairly compact form, and use function-like macros instead of structure-access syntax to access them.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thanks for the very good explanation! I use those constants in simulink. When generating C code from my simulink model i tell simulink those constants are defined in an external h file which it can include in its C files when generating. As i only link the generated code to a header file i wanted to have everything defined in that header file. It is true that your solution using a .h AND a .C together seems like the best answer. But now i need to figure out a way to tell the code generation tool to go look for a .c file too, which i dont know how to do (yet ;) ) Anyway, thanks !! – Hood Feb 26 '20 at 09:05
  • 1
    aaaaaand i figured it out! Thanks for your help! – Hood Feb 26 '20 at 09:37
  • 1
    C23 will add support for attributes including 'maybe_unused'. It could be used to portably silence compiler about unused 'static const' – tstanisl May 01 '21 at 12:14