Struct Initialization of the C/C++ Programming Language

How to initialize a struct in accordance with C programming language standards

In (ANSI) C99, you can use a designated initializer to initialize a structure:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

Other members are initialized as zero: "Omitted field members are implicitly initialized the same as objects that have static storage duration." (https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html)

How do I initialize a struct in C

I think there is a typo in the declaration of the data member arr. Instead of

char [3]arr;

you have to write

char arr[3];

You may not initialize an array with empty braces. So write for example

struct {
int a;
char arr[3];
.
.
.
} data = {
.a = 1,
.arr = { 0 }
};

In fact it is enough to write

struct {
int a;
char arr[3];
.
.
.
} data = {
.a = 1
};

The array implicitly will be initialized with zeroes.

Struct initialization of the C/C++ programming language?

The first statement creates a variable initialized to the given values, i.e., these values are built in memory and stored directly in the program executable in that variable address (for globals) or ready for memory copy (for stack variables).

The second statement of the second block is very different. Although it looks similar, it is an assign expression. It means that the RHS of the equals operator is an expression that is evaluated (independently of what is in the LHS of =), and then passed to the = operator. Without proper context, {...} doesn't have any meaning.

In C99, you can do this:

struct_name_id = (struct struct_type_id){ value1, value2, value3 };

Now the RHS of the equals operator is a valid expression, since there is proper context for the compiler to know what is in {...}.

In C++11, the syntax is:

struct_name_id = struct_type_id{ value1, value2, value3 };

Initialize a struct in C

This should work:

Rectangle world = {"World", {NULL, NULL}, {0, 0}, {5, 5}, 0};

Initializing a struct to 0

The first is easiest(involves less typing), and it is guaranteed to work, all members will be set to 0[Ref 1].

The second is more readable.

The choice depends on user preference or the one which your coding standard mandates.

[Ref 1] Reference C99 Standard 6.7.8.21:

If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

Good Read:
C and C++ : Partial initialization of automatic structure

C struct initialization using labels. It works, but how?

Here is the section of the gcc manual which explains the syntax of designated initializers for both structs and arrays:

In a structure initializer, specify the name of a field to initialize
with '.fieldname =' before the element value. For example, given the
following structure,

 struct point { int x, y; };

the following initialization

 struct point p = { .y = yvalue, .x = xvalue }; 

is equivalent to

 struct point p = { xvalue, yvalue }; 

Another syntax which has the same meaning, obsolete since GCC 2.5, is 'fieldname:', as shown here:

 struct point p = { y: yvalue, x: xvalue };

The relevant page can be found here.

Your compiler should have similar documentation.

Default initialization for a struct in C

Unfortunately, you can't, but if you do this a lot, you could create a constant that you use for initialization:

struct data_msg {
uint8_t id;
uint8_t data1;
uint32_t data2;
};

const struct data_msg dm_init = {.id = 25};

int main(void) {
struct data_msg var = dm_init; // var.id is now 25, data1 = 0, data2 = 0
// ...
}

C++ Structure Initialization

If you want to make it clear what each initializer value is, just split it up on multiple lines, with a comment on each:

address temp_addres = {
0, // street_no
nullptr, // street_name
"Hamilton", // city
"Ontario", // prov
nullptr, // postal_code
};

How to initialize a struct to 0 in C++

Before we start:

  1. Let me point out that a lot of the confusion around this syntax comes because in C and C++ you can use the = {0} syntax to initialize all members of a C-style array to zero! See here: https://en.cppreference.com/w/c/language/array_initialization. This works:

    // z has type int[3] and holds all zeroes, as: `{0, 0, 0}`
    int z[3] = {0};

    But, that syntax does not work the same for structs, which are entirely different animals than C-style arrays.

  2. See also my follow-up question I asked after writing this answer below: Why doesn't initializing a C++ struct to = {0} set all of its members to 0?


Back to the answer:

I figured it out: to get it to compile, just delete the zero:

# does NOT work
myStruct _m1 = {0};

# works!
myStruct _m1 = {};

It now compiles. However, I ran a bunch of tests to check some things in my struct_initialization.cpp file in my eRCaGuy_hello_world repo, and that does NOT initialize all elements of the struct to zero! Rather, it initializes the struct to its default values. To run my tests and see for yourself, clone my repo above and run eRCaGuy_hello_world/cpp/run_struct_initialization.sh.

Assuming you have this struct:

typedef struct
{
int num1 = 100;
int num2 = -100;
int num3;
int num4 = 150;
} data_t;

Note: the typedef above is a carry-over from when I was testing this stuff in C instead of C++ (although the default struct values are not allowed in C, of course). For C++, this is preferred instead:

struct data_t
{
int num1 = 100;
int num2 = -100;
int num3;
int num4 = 150;
};

So please ignore it wherever I unnecessarily use typedef to define the structs below.

Anyway, if I declare one of the above data_t structs, and then do this:

data_t d2 = {};
printf("d2.num1 = %i\nd2.num2 = %i\nd2.num3 = %i\nd2.num4 = %i\n\n",
d2.num1, d2.num2, d2.num3, d2.num4);

...the output will be:

d2.num1 = 100
d2.num2 = -100
d2.num3 = 0
d2.num4 = 150

And I'm not even sure if d2.num3 is zero because it was initialized to zero or because it was left uninitialized, and that memory location happened to contain zero.

As explained here: https://en.cppreference.com/w/cpp/language/zero_initialization, you can also do this:

myStruct _m1{};

In the example above, this code:

data_t d2{};
printf("d2.num1 = %i\nd2.num2 = %i\nd2.num3 = %i\nd2.num4 = %i\n\n",
d2.num1, d2.num2, d2.num3, d2.num4);

...would produce output identical to what I showed above.

Even in cases where setting the struct to = {0} DOES work, such as this:

// Does NOT do what I expected! Only sets the FIRST value in the struct to zero! 
// The rest seem to use default values.
data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
d3.num1, d3.num2, d3.num3, d3.num4);

...the output is still not what I expected, as it only sets the first value to zero! (I don't understand why):

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150

On C-style arrays, however (NOT structs), these semantics work fine. Refer to this answer here (How to initialize all members of an array to the same value?). The following lines, therefore, both set all elements of the C-style array to zero when using C++:

uint8_t buffer[100] = {0}; // sets all elements to 0 in C OR C++
uint8_t buffer[100] = {}; // sets all elements to 0 in C++ only (won't compile in C)

So, after much experimentation, it looks like the following several ways are the ONLY ways to zero-initialize a struct, PERIOD. If you know differently, please comment and/or leave your own answer here.

The only ways possible to zero-initialize a struct in C++ are:

  1. Be explicit:

     // C-style typedef'ed struct
    typedef struct
    {
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
    } data_t;

    // EXPLICITLY set every value to what you want!
    data_t d1 = {0, 0, 0, 0};
    // OR (using gcc or C++20 only)
    data_t d2 = {.num1 = 0, .num2 = 0, .num3 = 0, .num4 = 0};
  2. Use memset() to force all bytes to zero:

     data_t d3;
    memset(&d3, 0, sizeof(d3));
  3. Set all default values to zero in the first place:

     // C-style typedef'ed struct
    typedef struct
    {
    int num1 = 0;
    int num2 = 0;
    int num3 = 0;
    int num4 = 0;
    } data_t;

    // Set all values to their defaults, which are zero in
    // this case
    data_t d4 = {};
    // OR
    data_t d5{}; // same thing as above in C++

    // Set the FIRST value only to zero, and all the rest
    // to their defaults, which are also zero in this case
    data_t d6 = {0};
  4. Write a constructor for the C++ struct

     // 1. Using an initializer list
    struct data
    {
    int num1;
    int num2;
    int num3;
    int num4;

    data() :
    num1(0),
    num2(0),
    num3(0),
    num4(0) {}
    };

    data d7; // all values are zero

    // OR: 2. manually setting the values inside the constructor
    struct data
    {
    int num1;
    int num2;
    int num3;
    int num4;

    data()
    {
    num1 = 0;
    num2 = 0;
    num3 = 0;
    num4 = 0;
    }
    };

    data d8; // all values are zero
  5. Use a struct with no default values, and make your object you create from it static

     typedef struct
    {
    int num1;
    int num2;
    int num3;
    int num4;
    } data_t;

    // `static` forces a default initialization of zero for each
    // value when no other default values are set
    static data_t d9;
  6. So, if you have a struct with non-zero default values, and you want to zero all values, you must do it EXPLICITLY! Here are some more ways:

     // 1. Have a `constexpr` copy of the struct that you use to
    // reset other struct objects. Ex:

    struct data
    {
    int num1 = 1;
    int num2 = 7;
    int num3 = -10;
    int num4 = 55;
    };

    constexpr data DATA_ALL_ZEROS = {0, 0, 0, 0};

    // Now initialize d13 to all zeros using the above `constexpr` struct
    // object
    data d13 = DATA_ALL_ZEROS;

    // OR 2. Use a `zero()` member function to zero the values:

    struct data
    {
    int num1 = 1;
    int num2 = 7;
    int num3 = -10;
    int num4 = 55;

    zero()
    {
    num1 = 0;
    num2 = 0;
    num3 = 0;
    num4 = 0;
    }
    };

    data d14;
    d14.zero();

The big take-away here is that NONE of these: data_t d{}, data_t d = {}, and data_t d = {0}, actually set all members of a struct to zero!

  1. data_t d{} sets all values to their defaults defined in the struct.
  2. data_t d = {} also sets all values to their defaults.
  3. And data_t d = {0} sets only the FIRST value to zero, and all other values to their defaults.

SO, BE EXPLICIT!

Note that the above key take-aways I wrote seem to contradict this documentation on cppreference.com, so it has led me to ask this follow-up question listed just below, which has proven VERY helpful to my understanding!

Going further

  1. MOST USEFUL: Follow-up question of mine: Why doesn't initializing a C++ struct to = {0} set all of its members to 0?

References:

  1. VERY USEFUL:
    1. https://en.cppreference.com/w/cpp/language/zero_initialization
    2. https://en.cppreference.com/w/cpp/language/aggregate_initialization
    3. https://en.cppreference.com/w/cpp/language/value_initialization
  2. VERY USEFUL: Initializing all members of an array (not struct) to the same value:
    1. How to initialize all members of an array to the same value?
    2. [gcc only] How to initialize all members of an array to the same value?
  3. https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/cpp/struct_initialization.cpp
    1. Clone this repo and run the code yourself with cpp/run_struct_initialization.sh

Related:

  1. Initializing default values in a struct
  2. *****[my own answer, which demonstrate this sort of struct modification/aggregate member reassignment within any function: leds[0] = {10, 20, 30, 40, 50};] Arduino Stack Exchange: Initializing Array of structs

C Programming - Structs, Pointers, and Initialization

When you take a look at the:

typedef struct Members{
Count *member;
}Members;

please notice that member is a pointer pointing to the Count type object.
To be able to operate on that object the object has to exist. Memory for it has to be dynamically or statically allocated. Otherwise, the behavior of your program in undefined.

Check the memory allocation done in the sample program:

#include <stdio.h>
#include <stdlib.h>

typedef struct CharStruct{
char *names;
}CharStruct;

typedef struct Count{
CharStruct *arr;
int counter;
}Count;

typedef struct Members{
Count *member;
}Members;

typedef struct Test{
Members people;
}Test;

void set_members(struct Test *person)
{
int total = 0;
while(total < 10){
++total;
}
person->people.member->counter = total;
}

void print_total(struct Test *person)
{
printf("Total Members: %d\n", person->people.member->counter);
}

int main(void) {
printf("Hello!\n"); /* prints Hello */
Test person;
#if 0 // allocate memory on stack
Members m;
Count c;
// use declared variables
person.people = m;
m.member = &c;
#else
// or dynamic alocation
person.people.member = malloc(sizeof(Count));
#endif

set_members(&person);
print_total(&person);

person.people.member->counter = 77;
print_total(&person);

free(person.people.member);

//system("pause");
return EXIT_SUCCESS;
}

Output:

Hello!                                                                                                                                     
Total Members: 10
Total Members: 77

The memory for object of type Count where type Count

typedef struct Count{
CharStruct *arr;
int counter;
}Count;

was allocated by:

malloc(sizeof(Count));

It is worth to stress that you can use the int counterbut CharStruct *arr points to undefined memory location.

Again, to safely use member arr it has to point to a valid object of type CharStruct. You should allocate memory for the CharStruct. That is not all. Once you have object CharStruct it has char *names pointer for pointing to the string. If you want to use names it should point to the valid string!

Your chain of nested structures with pointers will require very careful memory handling for allocation and deallocation. Be aware of potential memory leaks or uninitialized pointers. Those are one of the most common problems encountered by many C programmers.



Related Topics



Leave a reply



Submit