Search code examples
arrayscheaderinitializer

How to build a complex array in a C header file?


I am trying to build an array in a C header file by using initializers. This could also be done in function using executable code but I think a header would be a more elegant solution. I am not very strong with data structures and struggle to find a solution when searching the web. My skeleton looks something like this:

typedef struct fontTypeDef {
    uint16_t   xOffset;
    uint16_t   yOffset;
    uint16_t   width;
    uint16_t   height;
    uint32_t*  bitmapPtr;
} fontTypeDef;

fontTypeDef font[128];

font[32] = {0, 0, 8, 1 {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0x0}};
font[33] = {1, 2, 3, 1 {0xABCDEF, 0x0, 0xFF, 0xAB, 0xCC, 0x12321}};
.
.
.

How is this done correctly and memory efficient?

Edit 1: A lot of good feedback, hinting at the fact that this cannot be accomplished solely in a header file. I should have also given a bit more background. I have written a Python script that converts a given true-type font into a header file (of course I can also write to a .c file or any combination thereof). The intent is to use bit blitting in an STM32 µC and take advantage of DMA 2D when writing text to an LCD. I saw sample code were a picture is simply bit blitted via utilizing the definition in a header file that looks like this:

const uint32_t RGB565_480x272[65280] =
{
0x7A537A53,
0x82538253,
0x82538253,
0x8A538253,
0x82538253,
0x82538253,
.
.
}

Edit 2: Here's the solution as outlined by @Eric Postpischil.

In the header file:

typedef struct fontTypeDef {
    uint16_t        xOffset;
    uint16_t        yOffset;
    uint16_t        width;
    uint16_t        height;
    const uint32_t *bitmapPtr;
} fontTypeDef;

extern const fontTypeDef font[128];

In the C file:

const fontTypeDef font[128] =
{
…
    [32] = {0, 0, 8, 1, (uint32_t []) {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0x0}},
    [33] = {1, 2, 3, 1, (uint32_t []) {0xABCDEF, 0x0, 0xFF, 0xAB, 0xCC, 0x12321}},
…
}

Solution

  • You can achieve the effect you want by putting this in a header file:

    typedef struct fontTypeDef {
        uint16_t        xOffset;
        uint16_t        yOffset;
        uint16_t        width;
        uint16_t        height;
        const uint32_t *bitmapPtr;
    } fontTypeDef;
    
    extern const fontTypeDef font[128];
    

    and this in one source file:

    const fontTypeDef font[128] =
    {
    …
        [32] = {0, 0, 8, 1, (const uint32_t []) {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0x0}},
        [33] = {1, 2, 3, 1, (const uint32_t []) {0xABCDEF, 0x0, 0xFF, 0xAB, 0xCC, 0x12321}},
    …
    }
    

    Notes:

    • This puts the declarations in a header file, making them visible to all source files that include the header. It puts the definition of the data in a source file, so that the array font will be defined only once in the whole program.
    • Although other comments and answers have cautioned against using “global” variables or file-scope identifiers, it is reasonable to define constant data this way. As long as you want the data to be built into the program permanently, rather than read from a reference file, this is fine.
    • const has been added since we are defining data that is fixed when (or before) the program is built, and it should not be changed by the program.
    • The pointer member is initialized using a compound literal. The syntax (type) { initial values… } creates an initialized object. The code above uses it to create an array of uint32_t. The array is automatically converted to a pointer to its first element, and that pointer initializes the bitmapPtr element.
    • The elements of the font array are initialized using the designated initializer syntax [index] = { initial values… }, where index gives the index of the array element being initialized. If you initialize all array elements in order, you can also simply use a list of { initial values… }, omitting the [index] =.
    • The semicolons after the initial values in your original code have been changed to commas, as this is now a list of initializers inside an array definition rather than a sequence of statements.