开发者

defining a 2D array with malloc and modifying it

How do i define a 2D array using malloc ? (let开发者_Go百科s 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!


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)))


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];


// 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.


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.


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 :)


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.


Although this is an old question, here is my different solution.

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

int main(int arvc, char* argv[])
{
    int (*a)[5][8];
    int i, j;

    a = (int (*)[5][8])calloc(5*8, sizeof(int));        
    for (i = 0; i < 5; i++) {
        for (j = 0; j < 8; j++)
            (*a)[i][j] = i *10 + j;
    }

    for (i = 0; i < 5; i++) {
        for (j = 0; j < 8; j++)
            printf("%d ", (*a)[i][j]);
        printf("\n");
    }

    return 0;
}

Compile and run

[user@buzz ~]$ gcc main.c -o main
[user@buzz ~]$
[user@buzz ~]$ ./main
0 1 2 3 4 5 6 7
10 11 12 13 14 15 16 17
20 21 22 23 24 25 26 27
30 31 32 33 34 35 36 37
40 41 42 43 44 45 46 47
[user@buzz ~]$
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜