-1

I'm having trouble trying to allocate a 2d array of char* and return it from a function.

//my return type needs to change to something like (char*)[]?
char **readData( size_t numRows, size_t numCols )
{
    //I think this needs to chage to something like this:
    //char* (*data)[numRows] = malloc(sizeof(char*) * numCols );
    char **data = malloc( sizeof(char*) * numRows * numCols );

    for( size_t r=0; r < numRows; ++r ) {
        for(size_t c=0; c < numCols; ++c) {     
            data[r*numRows+c] = strdup( getString() );

            //I'd like this to be:
            //data[r][c] = strndup( getString() );
        }
    }

    return data;
}

int main()
{
    size_t rows = 5;
    size_t cols = 7;
    char **data = readData( rows, cols );

    if( data != NULL ) {

        for( int r=0; r < rows; ++r ) {
            for( int c=0; c < cols; ++c ) {
                printf( "[%i][%i] = %s\n", r, c, data[r*rows+c] );
                free(data[r*rows+c]);
                data[r*rows+c] = NULL;

                //I'd really like this to be:
                //printf( "[%i][%i] = %s\n", r, c, data[r][c] );
                //free(data[r][c]);
                //data[r][c] = NULL;
            }
        }

        free(data);
        data = NULL;
    }

    return 0;
}

I have two questions:

1) The above code is broken somehow. The printf prints NULLs when I wouldn't expect it to. I must be screwing up the indexing someplace.

2) I believe with C99 I can use the more familiar data[][] notation to index the 2d array of pointers. I have the changes in comments in the above code but I don't know what to set the return type of the function as. How can I change my program to use the data[][] notation? It may not be possible since I do not know either array dimension at compile time.

I've looked at the following stack overflow questions but still can't seem to get my head around it: Why can't we use double pointer to represent two dimensional arrays?

How to return a two-dimensional pointer in C?

Why can't we use double pointer to represent two dimensional arrays?

Community
  • 1
  • 1
awm129
  • 305
  • 2
  • 11

2 Answers2

2

You have a two-dimensional grid of strings, so your data structure is char ***. The data on all levels is dynamically allocated.

data is a handle to a dynamic array of numRows rows. Each row data[r] is a handle to a dynamic array of numCols strings; these arrays must also be allocated explicitly. Each string data[r][c] is a handle to a dynamic array of chars, which you create via strdup.

So your code might look like:

char ***readData(size_t numRows, size_t numCols)
{
    char ***data = malloc(numRows * sizeof(*data));

    for (size_t r=0; r < numRows; ++r) {
        data[r] = malloc(numCols * sizeof(*data[r]));

        for(size_t c=0; c < numCols; ++c) {     
            data[r][c] = strdup(getString());
        }
    }

    return data;
}

int main()
{
    size_t rows = 5;
    size_t cols = 7;
    char ***data = readData(rows, cols);

    if (data != NULL) {
        for (int r = 0; r < rows; ++r) {
            for (int c = 0; c < cols; ++c) {
                printf("[%i][%i] = \"%s\"\n", r, c, data[r][c]);
                free(data[r][c]);
            }
            free(data[r]);
        }
        free(data);
    }

    return 0;
}
M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • Thanks! That makes perfect sense. – awm129 Nov 06 '15 at 18:52
  • There is no 2D "grid of strings" (whatever that would be). A pointer is not an array. As mush as a `_Complex` is not a `_Bool`. – too honest for this site Nov 06 '15 at 19:22
  • @Olaf. Super comment, really. The grid of strings is the use case: The OP wants to have a 7x5 matrix of strings. Please distinguish between domain and implementation language. And nowhere do I say that a pointer is an array. – M Oehm Nov 06 '15 at 19:32
-1

the following code implements a 2D array of numRows x numCols

You will need to add the error checking (returned value !=NULL) after each call to malloc() to assure the operation was successful.

It initially clears all the pointers so it will be easy to pass each pointer to free() without worrying about if the pointer actually contains a valid pointer (free() properly handles a NULL pointer)

#define _GNU_SOURCE

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

char * getString( void );

//my return type needs to change to something like (char*)[]?
char **readData( size_t numRows, size_t numCols )
{
    // following results in an array of pointers to char, 1 ptr per row
    char **data = malloc( sizeof(char*) * numRows );

    // set all row pointers to NULL, to make it easier to free all the allocations
    memset( data, 0x00, sizeof( char*) *numRows );

    for( size_t r=0; r<numRows; r++)
    { // for each row
        data[r] = malloc( sizeof( char *) * numCols) ;
        // set all column ptrs to NULL, to kame it easier to free all the allocations
        memset( data[r], 0x00, sizeof( char * ) * numCols) ;
    }


    // set all the column pointers in each row
    for( size_t r=0; r < numRows; ++r )
    {
        for(size_t c=0; c < numCols; ++c)
        {
            char ** pRow = data+r; // get ptr to row entry
            char ** pCol = pRow+c;    // get ptr to column entry
            *pCol = strdup( getString() );
        }
    }

    return data;
}
user3629249
  • 16,402
  • 1
  • 16
  • 17