2

I am trying to allocate a 2d contiguous array using C99 variable length arrays. Something like this:

size_t rows = 5, cols = 5;
double (*array)[cols] = malloc(rows * sizeof *array);

The trouble is, I want to do the allocation in a function. I'm guessing that in order to do so, I need to declare the array first and then pass its address to the function. I've been trying things like this:

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

void allocate(double *((*grid)[]), size_t cols)
{
    size_t rows = 5; // in practice this the result of a computation
    double (*array)[cols] = malloc(rows * sizeof *array);
    *grid = array;
}


int main ()
{
    size_t cols = 5;
    double (*grid)[cols];    // note: edited to fix typo (was [])
    allocate(&grid, cols);

    return 0;
}

My compiler (GCC 4.7.1) is giving me the following warnings/errors:

$ gcc -Wall -pedantic -std=c99 2d.c -o 2d
2d.c: In function ‘allocate’:
2d.c:8:5: error: invalid use of array with unspecified bounds
2d.c: In function ‘main’:
2d.c:16:5: warning: passing argument 1 of ‘allocate’ from incompatible pointer type [enabled by default]
2d.c:4:6: note: expected ‘double * (*)[]’ but argument is of type ‘double (**)[]’

I have tried many variations of this kind of thing but I am clearly missing something. I would like to have a contiguous 2d array accessible like grid[j][i] from main but perform the allocation in a separate function. How can I do this?

3
  • should be void allocate(double (**grid)[], size_t cols) Commented Jul 10, 2014 at 15:15
  • Try allocating for all rows first. Dereferencing to some index gives you column address. Allocate for all columns then. Commented Jul 10, 2014 at 15:16
  • Seems to me that returning the address of the first row (instead of having an "out parameter") would be easier, easier to write and easier to understand: In allocate simply return array; , and in main simply grid = allocate(...);. Ah: The return type would best be done with a typedef (@user3477950). Commented Jul 10, 2014 at 15:32

6 Answers 6

2
void allocate( size_t cols, double (**arr)[cols] )
{
  size_t rows = ...;
  *arr = malloc( sizeof **arr * rows );
}

Edit

Complete example:

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

size_t get_rows( void )
{
  return 5;
}

void allocate( size_t cols, double (**arr)[cols] )
{
  size_t rows = get_rows();
  *arr = malloc( sizeof **arr * rows );
  if ( *arr )
  {
    for (size_t r = 0; r < rows; r++ )
      for (size_t c = 0; c < cols; c++ )
        (*arr)[r][c] = r * cols + c;
  }
}

int main( void )
{
  size_t cols = 5;
  double (*arr)[cols] = NULL;
  allocate( cols, &arr );
  for ( size_t r = 0; r < get_rows(); r++ )
    for ( size_t c = 0; c < cols; c++ )
      printf( "arr[%zu][%zu] = %.2f\n", r, c, arr[r][c] );

  free( arr );
  return 0;
}
Sign up to request clarification or add additional context in comments.

8 Comments

presumably this means that it is unnecessary to pass the value cols to the function?
It is necessary to specify cols, hence why it's the first parameter to the function.
Thanks, I see that now. Impressive, I didn't know that you could use the value of a previous argument in the function signature like that.
Mainly because cols must be declared before it can be used in the array pointer declaration. Edit Per H&S, you could also use [*] to specify a variable length array dimension, but the version of gcc I'm using doesn't yet support that.
I like this ,but It seems that does not to compile with clang 3.1. (gcc is no problem)
|
2

Change

void allocate(double *((*grid)[]), size_t cols)   
                      //    ^grid is pointer to array of `double *` type 

to

void allocate(double (**grid)[], size_t cols)  
                      //   ^grid is a pointer to pointer to array of `double` type  

13 Comments

euuuuhhh... nope. This is completely wrong. This will not allow OP (or anyone else, for that matter) to allocate an array more than one dimension, at least not correctly.
@user3477950 there was a typo in my original code but now I've fixed it, this seems to work fine. What do you suggest is the problem?
@user3477950; Could you explain why this is wrong? Do you know where to use pointer to pointer ?
@TomFenech; No. In case of double (*array)[cols] = malloc(rows * sizeof *array); sizeof *array is correct. You could replace it with sizeof **grid.
also sizeof(**grid) It is not possible to calculate the size of the incomplete type(E.g double (**grid)[]) .
|
2

You definitely can return a pointer-to-multidimensional-VLA. I'm not sure if one can declare a function with such a return type, but hey, why not just return void * instead?

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

void *allocate(size_t n, size_t k)
{
    int (*p)[n] = malloc(k * sizeof *p);
    for (size_t i = 0; i < k; i++) {
        for (size_t j = 0; j < n; j++) {
            p[i][j] = i * j;
        }
    }

    return p;
}


int main(void)
{
    size_t n = 3, k = 5;
    int (*p)[n] = allocate(n, k);

    for (size_t i = 0; i < k; i++) {
        for (size_t j = 0; j < n; j++) {
            printf("%d ", p[i][j]);
        }
        printf("\n");
    }

    free(p);
    return 0;
}

8 Comments

Why one should want to return pointer from a function if there is an option to do the same without returning it ?
@haccks Because why not? For me, an allocator function conceptually should return the allocated pointer. I hate APIs where a pointer-to-pointer is dereferenced and filled in with a pointer to newly allocated memory. This is like asking "why is malloc declared as void *(size_t) instead of void(void **, size_t)?"
@user3477950 DANGER! You are returning a pointer to memory alloc'ed in stack. Your pointer will be dangling just after returning; and all the memory malloced (and their pointers stored there) will be leaking.
@rslemos Nope. Where would I?
@user3477950 oh, nevermind. I saw *p[n] instead of (*p)[n].
|
0

If you want to allocate a contiguous two-dimensional array (as C does with static 2D arrays), your malloc will look like

element_type *array = malloc(rows * cols * sizeof *array);

To access the array you'll have to do pointer arithmetic yourself:

*(array + i*cols + j);

The code you describe is more akin to jagged arrays.

2 Comments

I'm aware of this approach but I would much rather use the variable length array based approach as shown in the question, so that I can use two indices in the code rather than computing one. The first block of code in my question does not produce a jagged array, it produces a block of contiguous memory.
Jagged arrays are composed of rows + 1 blocks of contiguous memory. One block for the lines, and then rows blocks for each line.
0

The following works for me:

static void
xalloc(int rows, int cols, double (**array)[cols])
{
  *array = malloc(rows * sizeof(**array)) ;

  double* p = (void*)*array ;
  for (int i = 0 ; i < (rows * cols) ; ++i)
    p[i] = i ;
} ;

int
main()
{
  int rows = 10 ;
  int cols =  7 ;

  double (*array)[cols] ;
  xalloc(rows, cols, &array) ;

  for (int r = 0 ; r < rows ; ++r)
    {
      printf("%3d:", r) ;
      for (int c = 0 ; c < cols ; ++c)
        printf("%3d:%4.0f", c, array[r][c]) ;
      printf("\n") ;
    } ;
} ;

Which I think is consistent with the desire to use a Variably Modified type for the array.

Mind you, I also think it's horrible... but hey, it's not every day one gets to write something quite so abstruse as double (*array)[cols] and get away with it.

Comments

-1

Try changing your code like below.

void allocate (double ((**grid) []), size_t cols) {
    size_t rows = 5;
    double (*arr)[] = (double (*)[]) malloc (rows * sizeof (size_t));
    *grid = arr;
}

int main () {
    size_t cols = 5;
    double (*grid)[] = 0;
    allocate (&grid, cols);
    printf ("%x", (void *)grid); // to show that the memory is being allocated
    return 0;
}

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.