Reputation: 63
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
Upvotes: 0
Views: 11546
Reputation: 180181
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:
const
) structure type; andAs 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.
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.
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.)
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
.
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.
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.
Upvotes: 7