2

Let's say I have a struct made of several fields, all of the same basic data type.

For example:

struct myStruct {
    float a;
    float b;
    float c;
    float d;
    float e;
    float f;
}

Is there a smart approach to initialize or set all members to a given value, e.g. -1, or 0xDEADBEEF, in a way that is flexible to changes in the number of fields and in the field names?

Rationale:
Initializing all fields to an invalid state, and make sure all fields are initialized if later on I add new fields.

Note:
If there is a solution that would only work for integer types, I am anyway interested.

This is a different question from array initialization and zero-initialization of a struct, as here I am asking about initializing a struct, with fields all of the same basic data type, to a custom value.

This question, which doesn't concern arrays inside a struct, is also not answered at Initialize values of array in a struct. It's also not treated in structs in C with initial values, as I am asking about the case in which all data field have the same, basic, data type

Community
  • 1
  • 1
Antonio
  • 19,451
  • 13
  • 99
  • 197
  • @Skynet Well, that's for arrays, not structs... – Antonio Apr 11 '17 at 13:14
  • For integers you can of course always `memset()`, but that's rather blunt. It's better to use `= { 0 };` on the instance. – unwind Apr 11 '17 at 13:19
  • @Skynet That's for zero initialization – Antonio Apr 11 '17 at 13:20
  • @unwind I am not seeking for zero initialization. What would be a solution with memset for integer types other than chars? – Antonio Apr 11 '17 at 13:25
  • @Antonio Oh, that wasn't clear, 0 is the optimal initialization value (for reasons of BSS, for instance) and I didn't understand it wasn't OK to use. For larger integers you can `memset()` and have the 8-bit value repeated, so e.g. `0xffffffff` or `0x55555555` can work. I have no idea what criteria you have for a value to be OK to use as the "unused" marker, though. – unwind Apr 11 '17 at 13:27
  • @unwind Thanks, I think that will be the solution I will use in practice, i.e. choosing an invalid value at 0xffffff.... and creating an initializer with `memset(&instance,0xff,sizeof(instance));`. It won't work for floating point numbers, but solves my current problem. If you want to submit an answer in this sense, I'll accept it. – Antonio Apr 11 '17 at 14:44
  • Whoops, I linked to the wrong one, I meant this one: http://stackoverflow.com/questions/13706809/structs-in-c-with-initial-values – Toby Apr 11 '17 at 14:58
  • @Toby See my edit. I am referring to the case in which all members are of the same (basic) data type – Antonio Apr 11 '17 at 15:10
  • @unwind How do you like [this solution](http://stackoverflow.com/a/43352124/2436175)? – Antonio Apr 12 '17 at 16:24

3 Answers3

2

One way to address this problem is using the X Macro concept link1, link 2.

The list of struct members would be

#define LIST_OF_STRUCT_MEMBERS \
    X(a) \
    X(b) \
    X(c)

Then one would declare the struct as:

#define X(name) int name;
struct myStruct {
     LIST_OF_STRUCT_MEMBERS
}
#undef X

Where int could be replaced by any basic data type.

One could then create a const variable initialized to the default value:

#define X(name) -1,
const struct myStruct myStruct_DEFAULT_VALUE = { LIST_OF_STRUCT_MEMBERS };
#undef X

Translated by the preprocessor into:

const struct myStruct myStruct_DEFAULT_VALUE = {-1, -1, -1,};

This can be copied every time a new variable has to be initialized.

The method can easily be verified defining in the same way a print function:

#define X(name) printf("%s = %d\n", #name, in->name);
void printAllMembers(struct myStruct* in) {
     LIST_OF_STRUCT_MEMBERS
}    
#undef X

(The printAllMembers function might need some modification when the underlying basic data type is changed).

Verified here.


This concept can generalize to a struct with fields of different data types if we include the data type in LIST_OF_STRUCT_MEMBERS, e.g.:

#define LIST_OF_STRUCT_MEMBERS \
    X(int, a) \
    X(float, b) \
    X(double, c)

#define X(type, name) type name;
struct myStruct {
     LIST_OF_STRUCT_MEMBERS
};
#undef X

#define X(type, name) -1,
const struct myStruct myStruct_DEFAULT_VALUE = { LIST_OF_STRUCT_MEMBERS };
#undef X

#define intFormatting "%d"
#define floatFormatting "%f"
#define doubleFormatting "%f"

#define X(type, name) printf(#name " = " type ## Formatting "\n", in->name);
void printMyStruct (struct myStruct* in) {
     LIST_OF_STRUCT_MEMBERS
}
#undef X

Tested here.

Community
  • 1
  • 1
Antonio
  • 19,451
  • 13
  • 99
  • 197
1

There is no way to initialize the members of a struct to a given value, other than zero, independent of the number of fields of the struct.

However, you could develop a module that gives a new instance of the struct to the user code. The module now can initialize the members to any value you want and any change to the struct, in this respect, is limited to the module. For example:

// mystruct.h
struct MYSTRUCT {
    ...
} t_mystruct;
t_mystruct *newMyStruct(void);

// any module.c
#include "mystruct.h"

t_mystruct *newMyStruct(void)
{
    t_mystruct *newstruct;
    if ((newstruct= malloc(sizeof(t_mystruct)))==0) return 0;
    newstruct->member1= -1;
    newstruct->...
    return newstruct;
}
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • 1
    You could also write a default-initialization function for existing structures -- i.e. that does not allocate memory. That can be handy where one wants to avoid dynamic memory management. – John Bollinger Apr 11 '17 at 13:28
  • I think your answer is imprecise, namely when you say "other than zero": at least for integer types, I am aware of another 255 exceptions :) namely all the other values that can be set with `memset(&instance,value,sizeof(instance) );` with value in [1,255]. Of course, to perform this strictly at initialization time one has to first create an instance set at the default value using memset. – Antonio Apr 11 '17 at 15:34
  • Hi @Antonio, indeed, the word "initialize" is used strict and does not mean calling `memset`. Setting all fields to zero is possible; setting all fields to another value is not possible (using "initialization" as per the C language). Setting each field to its value is also possible, but that is not the same as "setting all fields to _a_ value". Besides, how to initialize a struct of floats to `0.1` using memset? – Paul Ogilvie Apr 11 '17 at 16:52
  • Initialization copying from an existing struct is initialization. Sure, the example I did is just for int. Anyway, I might have found a way to address my specific problem, please check my answer. – Antonio Apr 11 '17 at 17:02
0

Is there a smart way to initialize or set all members to a given value, e.g. -1, or 0xDEADBEEF, independently from the number of fields and from the field names?

No, there isn't, unless the value to which you want to initialize the fields is zero / NULL. In that case you can use an initializer such as { 0 }:

struct myStruct s = { 0 };

That says to initialize the first field to 0; since C does not permit partial initialization of aggregates, the other fields get the same value that they would get by default if they has static duration, which is also 0 / NULL (for scalars; the default initialization applies recursively to members that are themselves aggregates).

You can, however, provide a structure-specific initializer macro, or a global const object of the structure type that can be used to initialize the structure. If you maintain this together with the structure declaration then there is little risk of them falling out of sync. Moreover, you can initialize the members via designated initializers to avoid problems arising from the order of the members being altered. For example,

struct myStruct {
    float a;
    float b;
    float c;
};

#define MYSTRUCT_INITIALIZER { .a = 1.0, .b = 1.0, .c = 1.0 }

// ...

struct mystruct s = MYSTRUCT_INITIALIZER;

Note, too, that such an approach is more general than you asked for with respect to how you can initialize members (they don't have to all have the same type or all receive the same value).

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • This however doesn't address a key point of the question, i.e. it won't work "automatically" if new fields are added to the struct. – Antonio Apr 11 '17 at 13:27
  • @Antonio, of course this answer addresses that. It says "no" there isn't any way to do what you ask. The alternative I proposed is exactly that: an alternative. And even if fields are added to the structure without updating the initializer, it does not fail to initialize them; it simply initializes them according to the default scheme, which is already described. – John Bollinger Apr 11 '17 at 13:32
  • Anyway, the answer is "yes" – Antonio Apr 13 '17 at 12:19
  • @Antonio, the answer to the question *as posed* is "no". If the approach presented in your own answer serves you then that's well and good, but it solves a different problem than the one you presented. In particular, it relies not only on the struct members being known, but on them being memorialized in a macro. – John Bollinger Apr 13 '17 at 15:00
  • It's pretty clear that if a struct is called `myStruct`, I have the freedom to modify the way it is defined. – Antonio Apr 13 '17 at 15:05
  • I don't agree that the struct tag conveys that, @Antonio, but that's beside the point. Supposing that you do have that liberty, exercising it does not yield an initialization approach that is "independent[] from the number of fields and from the field names." – John Bollinger Apr 13 '17 at 15:16
  • I modified "independent" into "flexible". I believe this doesn't change substantially the nature of the question. – Antonio Apr 13 '17 at 15:44