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?

9
  • 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. Commented Nov 16, 2016 at 17:16
  • That is basically what I'm asking: How else can I store them in a struct? Commented Nov 16, 2016 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. Commented Nov 16, 2016 at 17:21
  • 1
    Change the member char * text to be a char (*)[3] as well: Make it char (*text)[3]. Commented Nov 16, 2016 at 17:26
  • 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 Commented Nov 16, 2016 at 17:31

3 Answers 3

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

Sign up to request clarification or add additional context in comments.

2 Comments

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

Comments

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 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.