Search code examples
arrayscstructconcatenationtypedef

How to merge two arrays of typedef structs in C


I know there are similar questions, but I'm really getting tripped up with the fact that I'm using structs, and I'm not very good at C in general.

I have two arrays of structs, and I'd like to concat them into one array of structs. I'm scripting video game commands, so one array is:

// Setup_Controller.c
command setup_controller[] = {
    { NOTHING,  250 },
    { TRIGGERS,   5 },
    { NOTHING,  100 },
    { TRIGGERS,   5 },
    { NOTHING,  100 },
};

and the other is

// Get_Eggs.c
command get_eggs[] = {
    // Face and activate the lady
    { LEFT,       5 },
    { NOTHING,    5 },
    { A,          5 },
    .
    . 
    .
    { LEFT,     250 },
    { NOTHING,   30 },
    { UP,        30 },
};

The typedef is right here:

// Joystick.h

// Declare Object...?
typedef struct {
    Buttons_t button;
    uint16_t duration;
} command; 

and my final execution is happening here // Joystick.c #include <stdlib.h>

#include "Joystick.h"

#include "./Tasks/Setup_Controller.c"
#include "./Tasks/Get_Eggs.c"

command * merge_arrays( command a[],    command b[],
                        size_t a_size,  size_t b_size ) {
    size_t c_size = a_size + b_size; 
    /*allocate new array of sufficient size*/
    command *c = malloc(c_size);
    unsigned i;
    /*copy elements from a into c*/
    for(i = 0; i<a_size; ++i) {
        c[i] = a[i];
    } 
    /*copy elements from b into c*/
    for(i = 0; i < b_size; ++i) {
        c[a_size+i] = b[i];
    }
    
    return c;
}

size_t setup_controller_size    = sizeof(setup_controller);
size_t get_eggs_size            = sizeof(    get_eggs    );

size_t total_size               = sizeof(setup_controller) + sizeof(    get_eggs    );

command step[total_size] = merge_arrays(setup_controller,      get_eggs,
                                        setup_controller_size, get_eggs_size);

I've been poking this code with a stick for hours now. I almost got it working with some code on how to merge arrays, but it kept throwing errors when I tried to take the size of one of these elements. That error was this:

invalid application of 'sizeof' to incomplete type 'command[]' {aka 'struct <anonymous>[]'}

All instead of array A and array B, to have an array C that contains first all the elements of A, then all the elements of B, preserving their original respective orders. Not sure why this turned into such trouble, but here we are.


Minimal Reproducible Example:

//// main.c ////

#include "main.h"

#include "script.c"

#include <stdlib.h>
#include <string.h>

void merge_arrays( data *a, data *b, data *c,
                   size_t a_size,  size_t b_size ) {
    
    memcpy(c, a, a_size);
    memcpy(((unsigned char *)c) + a_size, b, b_size);
}

size_t script1_size = sizeof(script1);
size_t script2_size = sizeof(script2);

merge_arrays(data *script1, data *script2, data *whole_thing,
             size_t a_size, size_t b_size);

.

//// main.h ////

typedef struct {
    int age;
    int height;
} data; 

.

//// script.c  ////

#include "script.h"

data script1[] = {
    { 123,  250 },
    { 123,   5 },
    { 123,  100 },
    { 123,   5 },
    { 123,  100 },
};

data script2[] = {
    { 123,  250 },
    { 123,   5 },
    { 123,  100 },
    { 123,   5 },
    { 123,  100 },
    { 123,  100 },
};

.

//// script.h //// 

extern data script1[];
extern data script2[];

Produced Errors:

.../Documents/test/main.c:20:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
merge_arrays(data *script1, data *script2, data *whole_thing,
^
/Users/.../Documents/test/main.c:20:1: error: conflicting types for 'merge_arrays'
/Users/.../Documents/test/main.c:10:6: note: previous definition is here
void merge_arrays( data *a, data *b, data *c,
     ^
1 warning and 1 error generated.

Solution

  • a_size and b_size are sizes in bytes, since that's what sizeof yields, but in the usual manner of array indexing in C, c[i] takes a count in number of elements. So if, for example, the size of command is 4, then you are indexing 4 times too many elements.

    It'd probably be more idiomatic to change merge_arrays to take a count of number of elements instead, and call it with sizeof(get_eggs) / sizeof(command) as an argument. Then make sure to adjust the malloc call to multiply its argument by sizeof(command).

    But even better would be to simply use memcpy, which works in bytes and is potentially more efficient:

    memcpy(c, a, a_size);
    memcpy(((unsigned char *)c) + a_size, b, b_size);
    

    The cast of c ensures that the + adds in units of bytes, and not in units of sizeof(command).