60

I am trying to create an array of strings in C using malloc. The number of strings that the array will hold can change at run time, but the length of the strings will always be consistent.

I've attempted this (see below), but am having trouble, any tips in the right direction will be much appreciated!

#define ID_LEN 5
char *orderedIds;
int i;
int variableNumberOfElements = 5; /* Hard coded here */

orderedIds = malloc(variableNumberOfElements * (ID_LEN + 1));

Ultimately I want to be able to use the array to do this:

strcpy(orderedIds[0], string1);
strcpy(orderedIds[1], string2);
/* etc */
Chris
  • 7,996
  • 11
  • 66
  • 98
  • 1
    Note that you shouldn't be using `int`s here. a) It's signed (and I doubt you want a -5 length string), and b) it's not guaranteed to be the right size to hold the values you need it to hold. Use the `size_t` type for storing array indices and object sizes. That's the type of the argument to `malloc`. – Chris Lutz May 09 '11 at 11:16
  • 4
    @Chris: that said, it's guaranteed to be big enough for `5`. – Steve Jessop May 09 '11 at 11:37

4 Answers4

98

You should assign an array of char pointers, and then, for each pointer assign enough memory for the string:

char **orderedIds;

orderedIds = malloc(variableNumberOfElements * sizeof(char*));
for (int i = 0; i < variableNumberOfElements; i++)
    orderedIds[i] = malloc((ID_LEN+1) * sizeof(char)); // yeah, I know sizeof(char) is 1, but to make it clear...

Seems like a good way to me. Although you perform many mallocs, you clearly assign memory for a specific string, and you can free one block of memory without freeing the whole "string array"

MByD
  • 135,866
  • 28
  • 264
  • 277
  • 5
    Do you have to use another for loop to free the memory you've allocated? – Minh Tran Sep 14 '15 at 02:30
  • 1
    Yes, using loops for performing the same operation multiple time is a good and common practice, which becomes a necessity when performing it a variable number of times. (But each for loop may be implemented as a while loop as well.) – MByD Sep 14 '15 at 21:14
9
char **orderIds;

orderIds = malloc(variableNumberOfElements * sizeof(char*));

for(int i = 0; i < variableNumberOfElements; i++) {
  orderIds[i] = malloc((ID_LEN + 1) * sizeof(char));
  strcpy(orderIds[i], your_string[i]);
}
sahaj
  • 822
  • 5
  • 17
7

Given that your strings are all fixed-length (presumably at compile-time?), you can do the following:

char (*orderedIds)[ID_LEN+1]
    = malloc(variableNumberOfElements * sizeof(*orderedIds));

// Clear-up
free(orderedIds);

A more cumbersome, but more general, solution, is to assign an array of pointers, and psuedo-initialising them to point at elements of a raw backing array:

char *raw = malloc(variableNumberOfElements * (ID_LEN + 1));
char **orderedIds = malloc(sizeof(*orderedIds) * variableNumberOfElements);

// Set each pointer to the start of its corresponding section of the raw buffer.
for (i = 0; i < variableNumberOfElements; i++)
{
    orderedIds[i] = &raw[i * (ID_LEN+1)];
}

...

// Clear-up pointer array
free(orderedIds);
// Clear-up raw array
free(raw);
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
0

#define ID_LEN 5
char **orderedIds;
int i;
int variableNumberOfElements = 5; /* Hard coded here */

orderedIds = (char **)malloc(variableNumberOfElements * (ID_LEN + 1) * sizeof(char));

..

Roman
  • 13,100
  • 2
  • 47
  • 63
  • It's not considered idiomatic C to cast the return value of `malloc`. Also, you're declaring `orderedIds` as an array rather than a pointer, which means you can't assign to it. Also, you're `malloc`ing one large block for all the character data, then casting it to a `char **` which is much different. – Chris Lutz May 09 '11 at 11:12
  • Changed *orderedIds[] to **orderedIds, though I believe that compiler wise it's the same, and the former is just more readable. Regarding the "one large block" - this is exactly what it is. Arrays are just memory blocks. – Roman May 09 '11 at 11:25
  • 2
    You're wrong. If you allocate it as a `char[][ID_LEN + 1]` you can't treat it as a `char **`. `char **` expects you to have pointers to data that is stored elsewhere. It isn't stored in dynamic memory the same way a two-dimensional array is stored on the stack. – Chris Lutz May 09 '11 at 11:27