17

I don't truly understand some basic things in C like dynamically allocating array of arrays. I know you can do:

int **m;

in order to declare a 2 dimensional array (which subsequently would be allocated using some *alloc function). Also it can be "easily" accessed by doing *(*(m + line) + column). But how should I assign a value to an element from that array? Using gcc the following statement m[line][column] = 12; fails with a segmentation fault.

Any article/docs will be appreciated. :-)

1
  • This is no "array of arrays". Commented Jun 24, 2016 at 14:27

7 Answers 7

35

The m[line][column] = 12 syntax is ok (provided line and column are in range).

However, you didn't write the code you use to allocate it, so it's hard to get whether it is wrong or right. It should be something along the lines of

m = (int**)malloc(nlines * sizeof(int*));

for(i = 0; i < nlines; i++)
  m[i] = (int*)malloc(ncolumns * sizeof(int));

Some side-notes:

  • This way, you can allocate each line with a different length (eg. a triangular array)
  • You can realloc() or free() an individual line later while using the array
  • You must free() every line, when you free() the entire array
Sign up to request clarification or add additional context in comments.

11 Comments

@jpalecek: The way I allocated memory for that was: m = malloc(lines * columns * sizeof(int *));
@hyperboreean: That would allocate a one dimensional array of pointers. What you want is an array of pointers that each point to another array. You need to first allocate the array of pointers, then allocate memory for each array that is being pointed to.
Wow, two pathetically useless edits. @chakrit: casting malloc is generally considered bad practice in C as it is not necessary and can mask errors. +1 for the original answer.
@Robert, it unfortunately depends on the compiler implementation. Some older compilers have non-standard mallocs that require a cast. VisualStudio was this way until fairly recently (can't remember the exact version)
Casting malloc is good practice in C++ because it is required if the variable on the LHS is not a void pointer. It is bad practice in C because the warnings it eliminates are almost always errors that need to be properly addressed and removing the warning just treats the symptom.
|
5

Your syntax m[line][colummn] is correct. But in order to use a 2D array in C, you must allocate memory for it. For instance this code will allocated memory for a table of given line and column.

int** AllocateArray(int line, int column) {
  int** pArray = (int**)malloc(line*sizeof(int*));
  for ( int i = 0; i < line; i++ ) {
    pArray[i] = (int*)malloc(column*sizeof(int));
  }
  return pArray;
}

Note, I left out the error checks for malloc for brevity. A real solution should include them.

1 Comment

There are several ways to dynamically create "multidimensional arrays" in C with subtle but important differences, see c-faq.com/aryptr/dynmuldimary.html for details. Also, as a warning to others regarding the dangers of casting malloc: c-faq.com/malloc/mallocnocast.html.
3

It's not a 2d array - it's an array of arrays - thus it needs the multiple allocations.

1 Comment

Probably this was my problem that I was getting a seg fault.
3

Here's a modified version of quinmars' solution which only allocates a single block of memory and can be used with generic values by courtesy of void *:

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

void ** array2d(size_t rows, size_t cols, size_t value_size)
{
    size_t index_size = sizeof(void *) * rows;
    size_t store_size = value_size * rows * cols;

    char * a = malloc(index_size + store_size);
    if(!a) return NULL;

    memset(a + index_size, 0, store_size);
    for(size_t i = 0; i < rows; ++i)
        ((void **)a)[i] = a + index_size + i * cols * value_size;

    return (void **)a;
}

int printf(const char *, ...);

int main()
{
    int ** a = (int **)array2d(5, 5, sizeof(int));
    assert(a);
    a[4][3] = 42;
    printf("%i\n", a[4][3]);
    free(a);
    return 0;
}

I'm not sure if it's really safe to cast void ** to int ** (I think the standard allows for conversions to take place when converting to/from void * ?), but it works in gcc. To be on the safe side, you should replace every occurence of void * with int * ...


The following macros implement a type-safe version of the previous algorithm:

#define alloc_array2d(TYPE, ROWS, COLS) \
    calloc(sizeof(TYPE *) * ROWS + sizeof(TYPE) * ROWS * COLS, 1)

#define init_array2d(ARRAY, TYPE, ROWS, COLS) \
    do { for(int i = 0; i < ROWS; ++i) \
        ARRAY[i] = (TYPE *)(((char *)ARRAY) + sizeof(TYPE *) * ROWS + \
        i * COLS * sizeof(TYPE)); } while(0)

Use them like this:

int ** a = alloc_array2d(int, 5, 5);
init_array2d(a, int, 5, 5);
a[4][3] = 42;

1 Comment

calloc allocates and zeroes the array. init_array2d is probably redundant, unless there is a version which might set each cell to a given value that is not (TYPE)0
1

Although I agree with the other answers, it is in most cases better to allocate the whole array at once, because malloc is pretty slow.


int **
array_new(size_t rows, size_t cols)
{
    int **array2d, **end, **cur;
    int *array;

    cur = array2d = malloc(rows * sizeof(int *));
    if (!array2d)
        return NULL;

    array = malloc(rows * cols * sizeof(int));
    if (!array)
    {
        free(array2d);
        return NULL;
    }

    end = array2d + rows;
    while (cur != end)
    {
        *cur = array;
        array += cols;
        cur++;
    }

    return array2d;
}

To free the array simply do: free(*array); free(array);

Note: this solution only works if you don't want to change the order of the rows, because you could then lose the address of the first element, which you need to free the array later.

1 Comment

That's a witty solution :) Although if I wanted to allocate it at once, I would probably resort to the "array[i*cols+j]" addressing and ditch the "array2d" array.
1

Humm. How about old fashion smoke and mirrors as an option?

#define ROWS  5
#define COLS 13
#define X(R, C) *(p + ((R) * ROWS) + (C))

int main(void)
{
    int *p = (int *) malloc (ROWS * COLS * sizeof(int));
    if (p != NULL)
    {
        size_t r;
        size_t c;
        for (r = 0; r < ROWS; r++)
        {
            for (c = 0; c < COLS; c++)
            {
                 X(r,c) = r * c;  /* put some silly value in that position */ 
            }
        }

        /* Then show the contents of the array */ 
        for (r = 0; r < ROWS; r++)
        {
            printf("%d ", r);   /* Show the row number */ 

            for (c = 0; c < COLS; c++)
            {
                 printf("%d", X(r,c));
            }

            printf("\n");
        }

        free(p);
    }
    else
    {
        /* issue some silly error message */ 
    }

    return 0;
}

Comments

0

Using malloc(3) for allocate the first array and putting in there pointers created by malloc(3) should work with array[r][c] because it should be equivalent to *(*(array + r) + c), it is in the C standard.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.