There are several approaches. The simplest thing is to declare an array of char arrays, for example:
char strs[N][M + 1]; // M is max length of each string ... strcpy(strs[i], "foo");
All memory for the strings is statically allocated, but the size of each row in the array is fixed, and you must have the size for the longest string, which can lead to some internal fragmentation. All lines are writable.
Another approach is to declare an array of pointers to char:
char *strs[N]; ... strs[i] = malloc(strlen("bar") + 1); if (strs[i]) strcpy(strs[i], "bar");
This way you can allocate as much or less memory as each row in the array requires, and if necessary, you can resize your rows. You can also point to string literals, but remember that literals may not be writable; that is, you cannot do something like:
strs[j] = "foo"; strs[j][0] = 'b'; // may not be allowed on string literal
You can dynamically distribute the whole breakdown:
char **strs = malloc(sizeof *strs * N); for (i = 0; i < N; i++) strs[i] = malloc(SIZE + 1);
This approach allows not only to resize each row as needed, but also to resize rows. Note that in this case, strs is not an array type, although it is treated as an array.
Note that the best methods regarding malloc () are different between C and C ++. In C, he considered bad practice to produce the result of malloc (). Firstly, this is not necessary since void pointers are implicitly thrown into other types of object pointers. For another, it will overwhelm diagnostics if you forget to # include stdlib.h or otherwise don't have a prototype for malloc () in scope. Remember that if C does not see the prototype of the function before the link, it will assume that the function returns an int, which cannot be implicitly converted to a pointer type.