I'm trying to initialise an array of 26 strings. I'd prefer not to place the array on the heap, but I get a segmentation fault when I try to assign memory to the array using memset
. The code to reproduce this is below:
char *string_array[26];
for (int x = 0; x < 26; x++)
memset(string_array[x], 0, 3 + x); //= calloc(3+x, sizeof(char));
If I change the code so that memory is assigned to the heap, with calloc
, I don't get a segmentation fault:
char *string_array[26];
for (int x = 0; x < 26; x++)
string_array[x] = calloc(3 + x, sizeof(char));
Why is that?
In the snippet using calloc
, you create 27 arrays: one array of pointers in automatic storage[1], and 26 arrays of chars on the heap.
In the snippet using memset
, you create the array of pointers, and that's it. You attempt to modify 26 arrays of chars, but you never created them.
The following would be equivalent to the calloc snippet, but using only automatic storage.
char string00[ 3 ] = { 0 };
char string01[ 4 ] = { 0 };
char string02[ 5 ] = { 0 };
char string03[ 6 ] = { 0 };
char string04[ 7 ] = { 0 };
char string05[ 8 ] = { 0 };
char string06[ 9 ] = { 0 };
char string07[ 10 ] = { 0 };
char string08[ 11 ] = { 0 };
char string09[ 12 ] = { 0 };
char string10[ 13 ] = { 0 };
char string11[ 14 ] = { 0 };
char string12[ 15 ] = { 0 };
char string13[ 16 ] = { 0 };
char string14[ 17 ] = { 0 };
char string15[ 18 ] = { 0 };
char string16[ 19 ] = { 0 };
char string17[ 20 ] = { 0 };
char string18[ 21 ] = { 0 };
char string19[ 22 ] = { 0 };
char string20[ 23 ] = { 0 };
char string21[ 24 ] = { 0 };
char string22[ 25 ] = { 0 };
char string23[ 26 ] = { 0 };
char string24[ 27 ] = { 0 };
char string25[ 28 ] = { 0 };
char *string_array[ 26 ] = {
string00, string01, string02, string03, string04,
string05, string06, string07, string08, string09,
string10, string11, string12, string13, string14,
string15, string16, string17, string18, string19,
string20, string21, string22, string23, string24,
string25,
};
Now, if we assume there's no alignment restriction for char
, we could simplify the above as follows:
char buffer[ 403 ] = { 0 }; // 3+4+5+...+28 = 403
char *string_array[ 26 ];
for ( size_t j=0, i=0; j<26; ++j ) {
string_array[ j ] = buffer + i;
i += j + 3;
}
Finally, as @Ted Lyngmo points out, some compilers provide alloca
/_alloca
to allocate in automatic storage.
char *string_array[ 26 ];
for ( size_t j=0; j<26; ++j ) {
string_array[ j ] = alloca( j + 3 ); // This is what you were missing.
memset( string_array[ j ], 0, j + 3 );
}
C doesn't have a concept of stack and heap. The C standard doesn't mandate where the objects are stored, only the duration for which they can be accessed. This is called storage duration.
So the following isn't based on the standard, but is nearly universally true:
objects with automatic storage duration | objects with allocated storage duration |
---|---|
On the stack, in a register, or optimized away | On the heap |
Needs less time/CPU to allocate | Needs more time/CPU to allocate |
Needs less space/memory to allocate | Needs more space/memory to allocate |
Limited control over lifespan of the allocated block | Lifespan can extend beyond the function that allocated the memory |
Uncatchable dire consequences to over-allocating | Over-allocating can be detected and handled |
Somewhat limited resource | Vast resource |
Always available | Not available on some systems (e.g. many embedded systems) |
Can be hard/impossible to allocate complex structures unless the compiler provides non-standard alloca or similar. |
Simple to allocate complex structures |
There are two storage durations not covered here: static and thread.