tags:

views:

126

answers:

6

Hello,

How do i define a 2D array using malloc ? (lets say 10X20).

second, can i increase number of rows or cols without creating a new increased array and copying all data to it?

for instance, how do i allocate memory so the array will be 10x30 or 15x20?

thank!

A: 

second, can i increase number of rows or cols without creating a new increased array and copying all data to it?

No, you can't change the size of an array. What you can do is use pointers and realloc to do so.

Tom
+2  A: 
// first allocate the 20 rows, that contain pointers to int 
// (if the matrix contains int type values)
int **a = malloc(20 * sizeof(int *));

// now loop for each row and allocate data for the actual values
int i;
for(i = 0; i < 20; i++) {
    a[i] = malloc(10 * sizeof(int));
}

For increasing the size of the matrix you can use realloc although it would probably be easier to regenerate the matrix of different size and copy the values over.

Luca Matteis
This pic looks like a C++ explanation. Note that if this is explaining the C++ `new` operator, it is wrong. `new` creates a *real* array of arrays, which will look like the first diagram (only allocating 3 * 4 integers, like declared arrays), but not like the second diagram (allocating 3 * 4 integers and 4 pointers). Of course the description is correct if applied to your loop code instead of `new int[3][4]`.
Johannes Schaub - litb
yes, i actually wanted to reference just the second diagram, let me get rid of it.
Luca Matteis
+1  A: 

While malloc() does not directly support multi-dimensional arrays, there are workarounds, such as:

int rows = 10;
int cols = 30;
int *array = malloc(rows * cols * sizeof(int));

// Element (5,6)
int x = 5;
int y = 6;
int element = array [ x * cols + y ];

While this isn't directly a 2D array, it works, and in my opinion it's the simplest. But if you want to use the [][] syntax instead, you would have to make pointers to pointers, for instance:

int rows = 10;
int cols = 30;
// Rows
int **array = malloc(rows * sizeof(int*));
// Cols
int i;
for(i = 0; i < rows; i++)
  array[i] = malloc(cols * sizeof(int));

// Element (5,6)
int x = 5;
int y = 6;
int element = array[x][y];
Frxstrem
+1 - I find this way much simpler and less error prone than the array of arrays solution.
cake
I think i will use your first example, it is much simpler. Thank u and all other for your answers
rob
mm just one more question, with the second example i can create a none square array?like in java:arr[][] = {1,2,3,4},{1,2}?
rob
@robYes of course, you can create jagged arrays in C as in java. In code line:array[i] = malloc(cols * sizeof(int))just substitute whatever number instead of COLS variable. That's it.
0x69
A: 

A 2D array is an 1D array of 1D arrays. As an array is simply a pointer, an array of arrays is an array of pointers. So, you use malloc to allocate an array of pointers (each representing a collumn), then use it again to allocate the individual arrays (each representing a row).

To expand/shrink the array, you use realloc(reference). Here's an example code:

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

int** alloc_matrix(int w, int h) {
    int** matrix;

    matrix = (int**) malloc(sizeof(int*) * h);
    for(int i = 0; i < h; i++) {
        matrix[i] = (int*) malloc(sizeof(int) * w);
    }

    return matrix;
}

void realloc_matrix(int*** matrix, int new_w, int new_h) {
    *matrix = (int**) realloc((void *)*matrix, sizeof(int*) * new_h);

    for(int i = 0; i < new_h; i++) {
        (*matrix)[i] = (int*) realloc((void *)(*matrix)[i], sizeof(int) * new_w);
    }
}

int main(int argc, char* argv[]) {
    // Allocating matrix
    int** m = alloc_matrix(10, 15);

    // Filling with some data
    for(int y = 0; y < 15; y++) {
        for(int x = 0; x < 10; x++) {
            m[y][x] = y * x; // Notice the index is [y][x] - NOT [x][y]!
        }
    }

    // Printing
    for(int y = 0; y < 15; y++) {
        for(int x = 0; x < 10; x++) {
            printf("%d\t", m[y][x]); // Notice the index is [y][x] - NOT [x][y]!
        }
        printf("\n");
    }

    // Reallocating
    realloc_matrix(&m, 20, 10);

    // Filling with some data
    for(int y = 0; y < 10; y++) {
        for(int x = 0; x < 20; x++) {
            m[y][x] = y * x; // Notice the index is [y][x] - NOT [x][y]!
        }
    }

    // Printing
    for(int y = 0; y < 10; y++) {
        for(int x = 0; x < 20; x++) {
            printf("%d\t", m[y][x]); // Notice the index is [y][x] - NOT [x][y]!
        }
        printf("\n");
    }
}

I'm sorry if I've got anything wrong. My C-fu is kinda rusty :)

PaoloVictor
+3  A: 

10x30:

int(*array)[30] = malloc((sizeof *array) * 10);

15x20:

int(*array)[20] = malloc((sizeof *array) * 15);

Resizing to 20x25:

int(*array2)[25] = realloc(array, (sizeof *array2) * 20);

The outer dimension (10, 15, 20) can be determined at runtime, because it's not need as part of index calculations by the compiler. The inner dimension (30, 20, 25) needs to be known at compile time. Hope it helps.

Note that unlike the array-of-pointer solutions, this one can be handled as a single block of memory, because it allocates everything in a single chunk of memory like a real declared array:

memcpy(somewhere, array2, sizeof(int) * 20 * 25); // (sizeof *array2) * 20

It ultimately depends on your usecase, though.


Since some people have difficulties understanding the actions taken by an index operation to array, let's what Clang gives us for an index expression in the following code

int main() {
  int(*array)[10] = malloc((sizeof *array) * 5);
  array[4][9] = 0;

  int(*array1)[10][5] = malloc((sizeof *array1) * 20);
  array1[19][9][4] = 0;
}

It's a nice compiler, which can print its AST in an easily readable manner

  // array[4][9] = 0;
  (BinaryOperator 0xba62cc0 <line:5:3, col:17> 'int' '='
    (ArraySubscriptExpr 0xba62c80 <col:3, col:13> 'int'
      (ImplicitCastExpr 0xba62c60 <col:3, col:10> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xba62c20 <col:3, col:10> 'int [10]'
          (DeclRefExpr 0xba62bdc <col:3> 'int (*)[10]' Var='array' 0xba62a00)
          (IntegerLiteral 0xba62c00 <col:9> 'int' 4)))
      (IntegerLiteral 0xba62c40 <col:12> 'int' 9))
    (IntegerLiteral 0xba62ca0 <col:17> 'int' 0))

  // array1[19][9][4] = 0;
  (BinaryOperator 0xba630b8 <line:8:3, col:22> 'int' '='
    (ArraySubscriptExpr 0xba63078 <col:3, col:18> 'int'
      (ImplicitCastExpr 0xba63058 <col:3, col:15> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xba63018 <col:3, col:15> 'int [5]'
          (ImplicitCastExpr 0xba62ff8 <col:3, col:12> 'int (*)[5]' <ArrayToPointerDecay>
            (ArraySubscriptExpr 0xba62fa0 <col:3, col:12> 'int [10][5]'
              (DeclRefExpr 0xba62f5c <col:3> 'int (*)[10][5]' Var='array1' 0xba62db0)
              (IntegerLiteral 0xba62f80 <col:10> 'int' 19)))
          (IntegerLiteral 0xba62fc0 <col:14> 'int' 9)))
      (IntegerLiteral 0xba63038 <col:17> 'int' 4))
    (IntegerLiteral 0xba63098 <col:22> 'int' 0)))

Note that each array subscript expression takes a pointer, adds the value of the index, and yields the addressed element. If that subelement is an array, it is decayed to a pointer to its first element. This is really not actually different than the steps done for a declared array

int main() {
  int array[5][10] = { };
  array[4][9] = 1;
}

Yields a very similar AST, with just the most inner expression first being decayed to a pointer to its first element

  // array[4][9] = 1;
  (BinaryOperator 0xbf9f7e8 <line:5:3, col:17> 'int' '='
    (ArraySubscriptExpr 0xbf9f7a8 <col:3, col:13> 'int'
      (ImplicitCastExpr 0xbf9f788 <col:3, col:10> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xbf9f748 <col:3, col:10> 'int [10]'
          (ImplicitCastExpr 0xbf9f728 <col:3> 'int (*)[10]' <ArrayToPointerDecay>
            (DeclRefExpr 0xbf9f6cc <col:3> 'int [5][10]' Var='array' 0xbfa81f0))
          (IntegerLiteral 0xbf9f6f0 <col:9> 'int' 4)))
      (IntegerLiteral 0xbf9f768 <col:12> 'int' 9))
    (IntegerLiteral 0xbf9f7c8 <col:17> 'int' 1)))
Johannes Schaub - litb
Wait, I'm a little confused. Isn't `int(*array)[30]` a pointer to array 30 of int? And you're assigning this pointer to a location of `sizeof(int) * 10` bytes big. This means that this location can only contain 10 ints. Where does the allocation happen for the rest of the bytes? Because it needs 30x10 ints (300 ints). I didn't know that putting the [] in the definition would replicate the assigned location to the arrays.... how does this work?
Luca Matteis
@Luca, yep it's a pointer to an array of 30 int. But `sizeof *array` yields `sizeof(int[30])` which is `sizeof(int) * 30`. I don't know what you mean by "would replicate the assigned location to the arrays". Lemme try explaining it: The first code simply allocates a buffer having size `sizeof(int[10][30])` and makes a pointer to `int[30]` point to it. You can then index in the range of `array[0..9][0..29]`.
Johannes Schaub - litb
Ohhhhhh, clever :). Thanks for teaching me something new. +1.
Luca Matteis
@Johannes: But wait.. can you still access a memory location using double pointer dereferencing, `array[5][10]`? I'm not sure how would that work since it's all just a single block of memory as you said. What would `array[5]` be?
Luca Matteis
`array[5]` would be `int[30]`. Note that it is `*(array + 5)`, which adds `sizeof(int[30]) * 5` bytes to the address stored in array. With the `int[30]` and the `int*` decay this yields, the second index operation can then take place and finally yield the `int` lvalue. This is how `int array[10][30];` works too. Once you refer to `array` in an index op, it decays and becomes `int(*)[30]`.
Johannes Schaub - litb
Note that array pointers are different from other data pointers. If `d` is a `int*`, then reading what `*d` refers to implies a memory read. But if `d` is an `T(*)[N]`, then "reading" (i.e adding something) what `*d` refers to implies no memory read. It just implies a decay of the `T[N]` to a `T*`. At times, I get a headache from this too :)
Johannes Schaub - litb
@Johannes, in your comment above I can see how `*(array + 5)` yields an `int[30]`, since you are dereferencing the pointer, but how does this yield ( or correspond to? ) an `int*` decay? "With the `int[30]` and the `int*` decay this yields... "
BeeBand
A: 

Instead of using int[row][col] you should better "wrap your matrix" into a one-dimensional array int[row*col].

Here is sample code:

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

#define DET_MALLOC_FAIL -1
#define DET_READ_FAIL -2
#define DET_WRITE_FAIL -3

typedef struct det_matrix_s {
    double * vect;
    size_t order;
} det_matrix;

void *det_sf_malloc(size_t dsize);
det_matrix * det_matrix_new(size_t order);
#define DET_MAT_ELEM(matr, i, j) \
    ((matr)->vect[((matr)->order * (i)) + (j)])
int det_matrix_read(det_matrix * matr, FILE * src);
int det_matrix_write(det_matrix * matr, FILE * dest);
void det_matrix_delete(det_matrix * matr);

/**
* Malloc wrapper
*/
void * det_sf_malloc(size_t dsize)
{
    void *data = malloc(dsize);
    if(NULL == data){
        exit(DET_MALLOC_FAIL);
    }
    return (data);
}

/**
* Allocates memory for a new matrix
*/
det_matrix * det_matrix_new(size_t order)
{
    det_matrix * res = det_sf_malloc(1 * sizeof(*res));
    double * vect = det_sf_malloc(order * order * sizeof(*vect));
    res->order = order;
    res->vect = vect;
    return (res);
}

/**
* Reads matrix 
*/
int det_matrix_read(det_matrix * matr, FILE * src)
{
    size_t i, j;
    if(NULL == matr || NULL == src){
        return (DET_READ_FAIL);
    }
    for(i = 0; i < matr->order; ++i){
        for(j = 0; j < matr->order; ++j){
            if(stdin == src){
                fprintf(stdout, "mat[%d][%d] = ", i, j);
            }
            fscanf(src, "%lf", &DET_MAT_ELEM(matr, i, j));
        }
    }
    return (EXIT_SUCCESS);
}

/**
* Writes matrix
**/
int det_matrix_write(det_matrix * matr, FILE * dest)
{
    size_t i, j;
    if(NULL == matr || NULL == dest){
        return (DET_WRITE_FAIL);
    }
    for(i = 0; i < matr->order; ++i){
        for(j = 0; j < matr->order; ++j){
            fprintf(dest, "%5.2lf ", DET_MAT_ELEM(matr, i, j));
        }
        fprintf(dest, "\n");    
    }
    return (EXIT_SUCCESS);
}

/**
* Free memory for matrix
*/
void det_matrix_delete(det_matrix * matr)
{
    free(matr->vect);
    free(matr);
}

/**
* Main
*/ 
int main(int argc, char * argv[])
{
    det_matrix * mat = det_matrix_new(3);
    det_matrix_read(mat, stdin);
    det_matrix_write(mat, stdout);
    det_matrix_delete(mat);
    return (EXIT_SUCCESS);
}

If re-allocation is an issue, than problems may appear. And you should use the bi dimensional version.

Andrei Ciobanu