1

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", "Cd", "Ef", "Gh"};

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("%zu: %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?

xZise
  • 2,321
  • 5
  • 22
  • 31
  • You have conceptual issue: You *cannot* store ***four*** `char` arrays (`{"Ab\0", "Cd\0", "Ef\0", "Gh\0"};` here) under ***one*** `char`-pointer (`char *text;` here) only, at least not in sane way. The compiler warnings are a hint to this issue. – alk Nov 16 '16 at 17:16
  • That is basically what I'm asking: How else can I store them in a struct? – xZise Nov 16 '16 at 17:21
  • OT: `"Ab\0"` is represented internally as `{'A', 'b', '\0', '\0'}`, so you probably do not want to place the `'\0'` explicitly. – alk Nov 16 '16 at 17:21
  • It's not a matter of whether you use a struct or not. The conceptual mistake lies in trying to use just a `char*`, which would point to exactly ***one*** `char`, or exactly ***one*** a C-"string". – alk Nov 16 '16 at 17:22
  • I don't understand what you are trying to say. I don't question the existence of a `struct`. I'm asking on how I can store this array in the `struct`, and it doesn't need to be a `char*`, but otherwise I wouldn't have this example code. – xZise Nov 16 '16 at 17:24
  • 1
    Change the member `char * text` to be a `char (*)[3]` as well: Make it `char (*text)[3]`. – alk Nov 16 '16 at 17:26
  • Referring my other comment (http://stackoverflow.com/questions/40638256/store-multidimensional-array-in-struct#comment68508942_40638256): The strings in the initialiser are of size ***4***. So you actually want the member (and the function parameter) to be `char(*text)[4]`. Or remove the explicit `'\0'` and do just: `...= {"Ab", "Cd", "Ef", "Gh"};`. – alk Nov 16 '16 at 17:30
  • 1
    when ever calling any of the heap memory allocation functions: (malloc, calloc, realloc), always check (!=NULL) the returned value to assure the operation was successful – user3629249 Nov 16 '16 at 17:31
  • @xZise What is the sense to keep a pointer to an array in the structure? In this case either all objects of the structure type will point to the same array or for each object of the structure type you have to create an array that will be pointed to by the structure data member. – Vlad from Moscow Nov 16 '16 at 17:47

3 Answers3

1

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).

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • I almost got it but placed the brackets wrongly (used `*(text[3])`). Appreciate the additional comments, though this code was only to extract the issue I had. – xZise Nov 16 '16 at 17:47
  • I know @xZise, that's why my last sentence! I had to look it up myself, that's why I linked you the answer I checked before writing this answer! :) Good question by the way, very refreshing! – gsamaras Nov 16 '16 at 17:51
1

this line:

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

is trying to initialize an array of 3 bytes in each entry with values that are 4 bytes in each entry.

note: "AB\0" is 4 bytes because declaring a char array Always appends a NUL byte to the end of the array.

Suggest:

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

The call to printf() contains several errors, which your compiler should have told you about.

The field: char *text; is actually pointing to a 2D array, so should be declared accordingly.

Need to perform error checking on the call to malloc().

here is a version of the code, will all (reasonable) corrections applied.

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

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


static char *some_text[] = {"Ab", "Cd", "Ef", "Gh"};

struct payload_s* create_payload(char **text, size_t length)
{
    payload *p = malloc(sizeof (struct payload_s));
    if( !p )
    {
        perror( "malloc for instance of payload failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, malloc successful

    p->text = text;
    p->length = length;
    return p;
}

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

The result of the above code is:

0: Ab
1: Cd
2: Ef
3: Gh
user3629249
  • 16,402
  • 1
  • 16
  • 17
0

You are using incompatible pointers that can not be converted implicitly to each other.

Character array some_text declared like

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

when it is used in expression as for example used as an argument it is implicitly converted to pointer to its first element and has type char ( * )[3]. It si not the same as pointer of type char *.

It looks like you need a structure with a flexible array.

Here is a demonstrative program that shows how such a structure can be used.

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

#define N   3

struct payload_s 
{
    size_t length;
    char text[][N];
};

struct payload_s * create_payload( char( *text )[N], size_t length )
{
    struct payload_s *p = malloc( sizeof( struct payload_s ) + 
                                  length * sizeof( char[N] ) );

    p->length = length;

    for ( size_t i = 0; i < length; i++ ) strcpy( p->text[i], text[i] );

    return p;
}

void free_payload( struct payload_s *p )
{
    free( p );
}

int main(void) 
{
    char some_text[4][N] = {"Ab", "Cd", "Ef", "Gh"};
    char another_text[5][N] = {"Bb", "Dd", "Ff", "Hh", "Jj"};

    struct payload_s *p1 = create_payload( some_text, 
        sizeof( some_text ) / sizeof( *some_text ) );

    struct payload_s *p2 = create_payload( another_text, 
        sizeof( another_text ) / sizeof( *another_text ) );

    for ( size_t i = 0; i < p1->length; i++ )
    {
        printf( "%s ", p1->text[i] );
    }
    printf( "\n" );

    for ( size_t i = 0; i < p2->length; i++ )
    {
        printf( "%s ", p2->text[i] );
    }
    printf( "\n" );

    free_payload( p2 );
    free_payload( p1 );

    return 0;
}

Its output is

Ab Cd Ef Gh 
Bb Dd Ff Hh Jj 
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335