There are several approaches. The simplest is to declare an array of arrays of char, like so:
char strs[N][M + 1]; // M is max length of each string
...
strcpy(strs[i], "foo");
All the memory for the strings is statically allocated, but the size of each string in the array is fixed, and you have to size for your longest possible string, which may result in some internal fragmentation. All strings 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 as little memory as each string in the array needs, and you can resize your strings if necessary. You can also just point to string literals, but remember that literals may not be writable; i.e., you may not be able to do something like:
strs[j] = "foo";
strs[j][0] = 'b'; // may not be allowed on string literal
You can dynamically allocate the whole smash:
char **strs = malloc(sizeof *strs * N);
for (i = 0; i < N; i++)
strs[i] = malloc(SIZE + 1);
This approach allows you to not only resize each string as necessary, but to resize the number of strings. Note that in this case, strs is not an array type, even though it is being treated as an array.
Note that best practices with respect to malloc() differ between C and C++. In C, it's considered bad practice to cast the result of malloc(). For one thing, it's unnecessary, as void pointers are implicitly cast to other object pointer types. For another, it will supress a diagnostic if you forget to #include stdlib.h or otherwise don't have a prototype for malloc() in scope. Remember that if C doesn't see a prototype for a function before it is referenced, it will assume that the function returns int, which cannot be implicitly converted to a pointer type.