7

I have a question regarding the initialization of an array of structs in C. Googling showed me that a lot of people have had very similar questions, but they weren't quite identical.

Essentially, I have a global array of structs of type "memPermissions" shown below. This array needs all the "address" and "ownerId" fields to be initialized to -1 upon program execution.

typedef struct memPermissions {
    int address;
    int ownerId;
} *test;

The problem is the array is sized using a #define, so I can't simply go:

#define numBoxes 5

struct memPermissions memPermissions[numBoxes] = {
{-1, -1},
...
{-1, -1}
};

I tried:

struct memPermissions memPermissions[numBoxes] = {-1, -1};

But naturally this only initialized the first element. (The rest were set to 0). The only solution that jumps to mind would be to initialize it with a simple loop somewhere, but because of the nature of where this code will run, I'm really hoping that's not my only option.

Is there any way to initialize all the elements of this array of structs without a loop?

Cheers, -Josh

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
JoshVarty
  • 9,066
  • 4
  • 52
  • 80
  • 2
    If all the numbers are -1 and all the variables are `int` (just int, short, long, long long or char), you can `memset` the array to -1 – Shahbaz Nov 20 '11 at 21:21
  • 1
    What is wrong with using a loop? – Jarek Nov 20 '11 at 21:21
  • Seems like you are trying to initialize the array of pointers? :) – Andrejs Cainikovs Nov 20 '11 at 21:22
  • @Shahbaz: `memset` works byte-for-byte, not `int`-for-`int`. – Fred Foo Nov 20 '11 at 21:22
  • 2
    @larsmans, yes, but four bytes of -1, are still -1 – Shahbaz Nov 20 '11 at 21:23
  • 2
    Loop is probably the best solution, easiest to read and it's what the machine is doing anyway – Martin Beckett Nov 20 '11 at 21:23
  • @Josh: what exactly is the problem with a loop? – Fred Foo Nov 20 '11 at 21:25
  • 3
    @MartinBeckett: No, if you have a static array value it is generally *not* set by a loop. It is set during program compilation and the array exists in the program image. It is loaded into RAM during program load. – Zan Lynx Nov 20 '11 at 21:26
  • Note that your `typedef` names the pointer to that structure type as `test`, but your code then proceeds to use `struct memPermissions`. You'd be better off with a simple '`struct memPermissions { ... };` than an unused `typedef`. The name `test` is not an obviously good choice for the pointer, either, even if you do need a name for the pointer (which I personally don't like very much). – Jonathan Leffler Nov 20 '11 at 21:32
  • 1
    The loop is an option, but I'm new to C and was hoping my ignorance was keeping me unaware of a cleaner solution. – JoshVarty Nov 20 '11 at 21:32
  • why not use {0, 0} instead? It'd be faster to initialize and is just as unlikely to be valid data? – Per Johansson Nov 20 '11 at 21:57
  • There might be a devious way to accomplish it with macros, but I'm not "up" on the C macro processor enough to attempt that. – Hot Licks Nov 20 '11 at 21:57
  • Like hot licks said, I'm sure you could find some sort of processor abuse to do it. – Kevin Nov 20 '11 at 22:05
  • You can't probably use preprocessor to solve this, because it forbids direct recursion and has no other means of iteration. – ulidtko Nov 20 '11 at 23:57

5 Answers5

8

The C99 standard added all sorts of useful ways to initialize structures, but did not provide a repeat operator (which Fortran has had since forever - but maybe that was why it wasn't added).

If you are using a sufficiently recent version of GCC and you can afford to use a non-portable extension, then GCC provides an extension. In the GCC 8.1.0 manual (§6.27 Designated Initializers), it says:

To initialize a range of elements to the same value, write ‘[first ... last] = value’. This is a GNU extension. For example,

int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };

If the value in it has side-effects, the side-effects will happen only once, not for each initialized field by the range initializer.

So, using this in your example:

struct memPermissions memPermissions[numBoxes] =
{
    [0..numBoxes-1] = {-1, -1},    // GCC extension
};

I wish this were in the C Standard; it would be so helpful!


Without using that or other similar compiler-specific mechanisms, your only choice is a loop. For a complex initializer with many fields, not all the same value, you can probably use:

#include <string.h>
#include "memperm.h"  // Header declaring your types and variables

static int initialized = 0;
// -2 so the initialization isn't uniform and memset() is not an option
static const struct memPermissions initPermissions = { -1, -2 };
struct memPermissions memPermissions[numBoxes];

void initialize_permissions(void)
{
    if (initialized == 0)
    {
        for (int i = 0; i < numBoxes; i++)
            memmove(&memPermissions[i], &initPermissions, sizeof(initPermissions));
        initialized = 1;
    }
}

You can also use memcpy() here - there is no danger of the two variables overlapping.

Now you just need to ensure that initialize_permissions() is called before the array is used - preferably just once. There may be compiler-specific mechanisms to allow that, too.

You could use a local variable in the initialize_permissions() function in place of the initialized static constant variable - just be sure your compiler doesn't initialize it every time the function is called.

If you have a C99 compiler, you can use a compound literal in place of the constant:

void initialize_permissions(void)
{
    if (initialized == 0)
    {
        for (int i = 0; i < numBoxes; i++)
            memmove(&memPermissions[i],&(struct memPermissions){ -1, -2 },
                    sizeof(memPermissions[0]));
        initialized = 1;
    }
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2

You can write an external program that is passed the number of items that you want. This program should be called by your Makefile or equivalent. The program will write an include file for you with the required number of -1 values as well as the #define.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
1

If you have the standard library available, you can use memset combined with sizeof(struct memPermissions) * numBoxes to fill your array with any uniform byte value. Since -1 is 0xFFFFFFFF on many platforms, this might work for you.

James
  • 2,373
  • 18
  • 16
0

The only solution that jumps to mind would be to initialize it with a simple loop somewhere

Which is the only possibility within the language, I'm afraid. In C, you either initialize each element explicitly, initialize to all zeros or don't initialize.

However, you can sidestep the issue by using 0 for the purpose that your -1 currently serves.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 2
    The issue was best solved in my case by using a third field within the structure initialized to 0. Unfortunately, I couldn't use either of the two existing fields because 0 was a possible value for them to be set to later in program execution. However, since your answer put me on track to solve this, I accepted it. Thanks! – JoshVarty Nov 20 '11 at 22:28
-1

If it really important to not use a loop, you could do something rather strange, and use/abuse memset assuming that is available.

N.B. Memset may be implemented using a loop, so it may be moot.

memset(memPermissions, 0xFF, sizeof(memPermissions)*numBoxes*2*sizeof(int));

The times 2 is needed for both members of the struct (i.e. two of them).

This is poorly designed in that it depends that the struct is not padded or aligned, which the compiler is free to do so as per the C specifications.

(Utilizing that -1 is typically 0xFFFFFFFF for 2-compliment negative integers on 32-bit processors, with 32-bit int. Kudos to @James for pointing this out.)

Though I would suspect in most cases that the code would be implemented as a small, fast, tight loop (rep movsd for x86) in assembly language in all but the most trivial of cases (very small values of numBoxes).

mctylr
  • 5,159
  • 20
  • 32