0

I used C's variable length array to implement an algorithm:

int matrix[rows][cols];

I managed to test that this does fail for ridiculous dimensions. Is there a way to allocate this matrix on heap instead of stack? Otherwise I'll have to rewrite this to int**...

Something like calloc(sizeof(int[rows][cols]), 1)? Note that this question is specifically about variable length arrays.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • @user3528438 I am asking **specifically** about variable length array data type. Besides, 5D, seriously? – Tomáš Zato Nov 28 '16 at 20:14
  • 4
    What works for 5D can be reduced to work for 2D. Did you try `calloc(sizeof(int[rows][cols]), 1)`? What size did you get? Actually, more relevant is to print the values of `rows`, `cols` and the `sizeof` expression. Does it give you what you need? Is the question "What do I assign the result to"? – Jonathan Leffler Nov 28 '16 at 20:18
  • @TomášZato 5D? Yes, seriously! Why not? – Jean-Baptiste Yunès Nov 28 '16 at 20:21
  • @user3528438 I misread the other post, I thought the dimensions are constants (they were upper-case). – Tomáš Zato Nov 28 '16 at 20:25
  • There are various duplicates of this question. I just saw one asked today in the past 6 hours. I flagged to that particular one because 1) I answered it using the VLA approach; 2) 5D is such a unique keyword that makes it easy to look up. However the VLA approach has two versions: 1) use a pointer to N-D array; 2) use pointer to (N-1)-D array and keep the top dimension a pointer. The former has the advantage of having the size of all dimensions recorded as the type of the pointer, the latter has the advantage of having the same syntax as regular N-D array rather than (N+1)-D – user3528438 Nov 28 '16 at 20:44

2 Answers2

3

It looks simple enough. The only remotely tricky bit is the type to hold the pointer to the dynamically allocated array:

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

static void print_matrix(int r, int c, int matrix[r][c])
{
    for (int i = 0; i < r; i++)
    {
        for (int j = 0; j < c; j++)
            printf(" %d", matrix[i][j]);
        putchar('\n');
    }
}

static void set_matrix(int r, int c, int matrix[r][c])
{
    for (int i = 0; i < r; i++)
    {
        for (int j = 0; j < c; j++)
            matrix[i][j] = (i+1) * 100 + j + 1;
    }
}

int main(void)
{
    size_t rows = 9;
    size_t cols = 7;
    size_t size = sizeof(int[rows][cols]);
    printf("rows = %zu, cols = %zu, size = %zu\n", rows, cols, size);
    int (*matrix)[cols] = calloc(sizeof(int[rows][cols]), 1);
    if (matrix != 0)
    {
        set_matrix(rows, cols, matrix);
        print_matrix(rows, cols, matrix);
        free(matrix);
    }
    return 0;
}

This code carefully uses calloc() to zero all the elements of the array, and then calls set_matrix() to set them to non-zero values. As written, malloc() would be better than calloc(), but the question used calloc() and it would not be hard to make it sensible for use with this code too (for example, a conditional assignment in set_matrix(), such as if (i && j && i != j)).

Example output:

rows = 9, cols = 7, size = 252
 101 102 103 104 105 106 107
 201 202 203 204 205 206 207
 301 302 303 304 305 306 307
 401 402 403 404 405 406 407
 501 502 503 504 505 506 507
 601 602 603 604 605 606 607
 701 702 703 704 705 706 707
 801 802 803 804 805 806 807
 901 902 903 904 905 906 907
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • You could create `matrix` a bit simpler with `int (*matrix)[rows][cols] = malloc(sizeof *matrix);` and next use `*matrix` instead of `matrix` while calling `set_matrix()` and `print_matrix()` – tstanisl Sep 16 '20 at 12:02
1

You can create a pointer to a VLA:

size_t rows, cols;
... // get values for rows and cols
T (*arr)[cols] = malloc( sizeof (T [cols]) * rows );
if ( arr )
{
  ...
  arr[i][j] = some_value; 
  ...
}

There is some debate over whether

T (*arr)[cols] = malloc( sizeof *arr * rows );

should work. The way the standard is worded, this form results in undefined behavior, since sizeof has to evaluate *arr at runtime (since the expression *arr refers to a VLA), and arr is an invalid pointer when sizeof *arr is evaluated.

However, it depends on what "evaluate" means in that particular context; there's no reason to have to dereference arr in order to determine the size of the array it points to, any more than you would for a fixed-length array:

T (*arr)[10] = malloc( sizeof *arr * rows ); 

I and a few others are of the opinion that the standard is poorly worded in this respect, and that sizeof *arr should be valid whether arr points to a fixed- or variable-length array. This is the idiom I use and it hasn't failed on me...yet.

But, I would be remiss if I didn't point out this issue, and provide you with something that I know won't result in UB.

John Bode
  • 119,563
  • 19
  • 122
  • 198