xZise xZise - 24 days ago 8
C Question

Store multidimensional array in struct

I have a two dimensional array where the first dimension has a variable length but the second dimension is fixed. Now in a function call I could do something like

char foo[][3]
but what is the corresponding definition in a
struct
?

So in the example code I expected it to print each string in a line, but as expected it treats the stored pointer as a single dimensional array.

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

struct payload_s {
size_t length;
char *text;
};

typedef struct payload_s payload;

static char some_text[4][3] = {"Ab\0", "Cd\0", "Ef\0", "Gh\0"};

payload* create_payload(char *text, size_t length)
{
payload *p = malloc(sizeof *p);
p->text = text;
p->length = length;
return p;
}

int main()
{
payload *p = create_payload(some_text, 4);
for (size_t i = 0; i < p->length; ++i)
printf("%d: %s\n", i, &p->text[i]);
}


I mainly noticed this because of a warning:

strut.c: In function ‘main’:
strut.c:23:33: warning: passing argument 1 of ‘create_payload’ from incompatible pointer type [-Wincompatible-pointer-types]
payload *p = create_payload(some_text, 4);
^~~~~~~~~
strut.c:13:10: note: expected ‘char *’ but argument is of type ‘char (*)[3]’
payload* create_payload(char *text, size_t length)
^~~~~~~~~~~~~~


I can get rid of this warning when the function is actually defined as
payload* create_payload(char text[][3], size_t length)
, but then there is a warning a few lines later and the behavior didn't change:

strut.c: In function ‘create_payload’:
strut.c:16:13: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
p->text = text;
^


Is the only solution to manually increment the pointer by the length of each value?

Answer

Use:

char (*text)[3];

instead of:

char *

since what you want here is a pointer to your 2D array, not a pointer to single char. Read more in C pointer to two dimensional array.

Of course, it is suggested to use a define, or something similar for your dimension, instead of the hardcoded 3, like in my example.


Min. Example:

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

#define M 3

struct payload_s {
    size_t length;
    char (*text)[M]; // change the member!
};

typedef struct payload_s payload;

// not need for null terminators in the strings,
// it will be placed automatically
static char some_text[4][M] = {"Ab", "Cd", "Ef", "Gh"};

// change the prototype as well
payload* create_payload(char (*text)[M], size_t length)
{
    payload *p = malloc(sizeof *p);
    p->text = text;
    p->length = length;
    return p;
}

int main()
{
    payload *p = create_payload(some_text, 4);
    for (size_t i = 0; i < p->length; ++i)
        // no need to print the address now
        // also 'zu' should be used for 'size_t'
        printf("%zu: %s\n", i, p->text[i]);
}

Output:

Georgioss-MacBook-Pro:~ gsamaras$ gcc -Wall main.c 
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out 
0: Ab
1: Cd
2: Ef
3: Gh

PS - Check what malloc() returns, to see if the memory was actually allocated (of course on real code, not in min. egs).