简体   繁体   中英

C Malloc Multidimensional Char Array

I would like to dynamically allocate (malloc) a multidimensional character array in C. The array would have the following format:

char *array[3][2] = {
    {"one","two"},
    {"three","four"},
    {"five","six"}
};

Before the array would be created, I would already know the number of rows and the lengths of all of the characters arrays in the multidimensional array. How would I malloc such a character array?

Thanks in advance!

This is one way to allocate a two dimensional array of char *.

Afterwards, you can assign the contents like a[1][2] = "foo"; Note that the elements of the array are initialized to (char *)0.

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

char ***alloc_array(int x, int y) {
    char ***a = calloc(x, sizeof(char **));
    for(int i = 0; i != x; i++) {
        a[i] = calloc(y, sizeof(char *));
    }
    return a;
}

int main() {
    char ***a = alloc_array(3, 2);
    a[2][1] = "foo";
    printf("%s\n", a[2][1]);
}

[Charlies-MacBook-Pro:~] crb% cc xx.c
[Charlies-MacBook-Pro:~] crb% a.out
foo

First of all, arrays are typically stored in Row Major form, so in reality you have a vector six elements long, each entry is a char * ptr. That is, the elements labelled by row, column are similar to:

 char *r1c1, *r1c2, *r2c1, *r2c2, *r3c1, *r3c1;

Thus, do a SIMPLE malloc of:

 char *matrix = malloc(3*2*sizeof(char *));

Then set the elements as:

 matrix[0] = "one";
 matrix[1] = "two";
 matrix[2] = "three";
 matrix[3] = "four";
 matrix[4] = "five";
 matrix[5] = "six";

Finally, to test this write a nested loop as:

 for (int r=0; r<3; r++)
 {
  for (int c=0; c<2; c++);
  {
    printf("%s\n",matrix[r][c]);
  }
 }

Note, how a matrix is treated first as a vector then as a matrix. C doesn't care!!

char *array[3][2] is nothing but a two dimensional array of pointers. Hence you need the storage space of 3*2*sizeof(char *) to store the pointers.

As you mentioned, the pointers are actually pointing to zero-terminated strings and you may like the strings to be malloc'ed as well. Assuming the total length of all the strings to be N (including zero-termination), the storage space needed is (3*2*sizeof(char *) + N).

Allocate memory for the above mentioned size and the copy the strings yourselves as below.

In the following code, we assume that the number of columns (2) is a constant

char *(*dst)[2] = (char *(*)[2]) malloc(3*2*sizeof(char *) + N);
char * s = ((char *) dst) + (3*2*sizeof(char *));
for (i = 0; i < 3; i++)
{
    for (j = 0; j < 2; j++)
    {
        strcpy(s, src[i][j]);
        dst[i][j] = s;
        s += strlen(s)+1;
    }
}

NOTE: In the above code, 'dst' is a pointer that points to the first row of the 2D array of char *.

If the number of columns is not constant, the syntax changes a bit, but the storage size is the same.

char **dst = (char **) malloc(3*2*sizeof(char *) + N);
char * s = ((char *) dst) + (3*2*sizeof(char *));
for (i = 0; i < 3; i++)
{
    for (j = 0; j < 2; j++)
    {
        strcpy(s, src[i][j]);
        dst[i*2 + j] = s; /* 2 is the number of columns */
        s += strlen(s)+1;
    }
}

NOTE: Here 'dst' is a pointer that points to the first element of 1D array of char * and the 2D indexing is done manually.

The above examples assume that the string lengths will not change after allocation. If the strings can change at any point in time after allocation, then it is better to allocate for each string separately.

Keep it simple, Sheldon. The answer you've selected uses a char *** , which is not even close to the equivalent of a char *[2][3] . The difference is in the number of allocations... An array only ever requires one.

For example, here's how I'd retro-fit the answer you selected. Notice how much simpler it is?

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

void *alloc_array(size_t x, size_t y) {
    char *(*a)[y] = calloc(x, sizeof *a);
    return a;
}

int main() {
    char *(*a)[2] = alloc_array(3, 2);
    a[2][1] = "foo";
    printf("%s\n", a[2][1]);
}

In case you arrive in this page, wanting to create an array like int myarray[n][M] (which is slighly different from the question since they want an array of string), where M is fixed and n can vary (for example if you want an array of coordinates...), then you can just do:

int (*p)[M] = malloc(n*sizeof *p);

and then use p[i][j] as before. Then, you will get sizeof p[i] = M*sizeof(int) :

#include <stdio.h>
#include <stdlib.h>
#define M 6
int main(int argc, char *argv[])
{
    int n = 4;
    int (*p)[M] = malloc(n*sizeof *p);
    printf("Size of int: %lu\n", sizeof(int));
    printf("n = %d, M = %d\n", n, M);
    printf("Size of p: %lu (=8 because pointer in 64bits = 8 bytes)\n", sizeof p);
    printf("Size of *p: %lu (=M*sizeof(int) because each case is an array of length M)\n", sizeof *p);
    printf("Size of p[0]: %lu (=M*sizeof(int) because each case is an array of length M)\n", sizeof p[0]);
    // Assign
    for (int i=0; i<n; i++) {
        for (int j=0; j<M; j++) {
            (p[i])[j] = i*10+j;
        }
    }
    // Display
    for (int i=0; i<n; i++) {
        for (int j=0; j<M; j++) {
            printf("%2d; ", (p[i])[j]);
        }
        printf("\n");
    }
    return 0;
}

which gives:

Size of int: 4
n = 4, M = 6
Size of p: 8 (=8 because pointer in 64bits = 8 bytes)
Size of *p: 24 (=M*sizeof(int) because each case is an array of length M)
Size of p[0]: 24 (=M*sizeof(int) because each case is an array of length M)
 0;  1;  2;  3;  4;  5; 
10; 11; 12; 13; 14; 15; 
20; 21; 22; 23; 24; 25; 
30; 31; 32; 33; 34; 35; 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM