I want to store multiple strings (aka arrays of chars) in an array. Therefore, I want to use a 2D-Array. Since I do not know the exact sizes (neither the length of the individual strings nor the number of strings), I need to increase the size dynamically.
For a better structure, I want to this inside a separate function. However, if I access my Array after resizing it, apparently the size has not changed, and I get a segmentation fault.
I am fairy new to C (coming from a C background)
#include<stdio.h>
#include <stdlib.h>
//I want to increase my array from [1][5] to [2][5]
void increase(char*** data)
{
*data = realloc(*data, 2 * sizeof (char*));
*data[1] = calloc(5, sizeof(char));
}
int main(void)
{
char** data = calloc(1, sizeof(char*));
data[0] = calloc(5, sizeof(char));
increase(&data);
data[1][3] = 'a'; //<-- When I access the array I get an segmentation fault
free(data);
return 0;
}
Is my approach with a char***
correct? I could implement it in a test environment directly in the main function and it worked. But, as soon as encapsulated it inside a function, I got a seg. fault.
I assume it has something to do with the way I pass the array to the function, but I cannot figure out what the cause of this is and how to solve it. Has anyone some ideas or maybe a solution?
CodePudding user response:
The error (which is subtly catastrophic) is in how you are attempting to allocate the second element of your passed pointer array, and is related to operator precedence. Note that the array subscript operator, []
has higher precedence than the indirection operator, *
.
Thus, your line:
*data[1] = calloc(5, sizeof(char));
actually has the effect of:
*(data[1]) = calloc(5, sizeof(char));
This, as you can hopefully see, is really not what you want to do.
Instead, you need:
(*data)[1] = calloc(5, sizeof(char));
Another problem (though not an error, per se) is that you aren't doing any error checks on your allocations; this is especially important when using realloc
because, as your code stands, you will lose the 'original' pointer if that call fails. It is better to store the return value from realloc
in a temporary, then overwrite the original only if that temporary is not NULL
.
Here's a possible re-working of your code (you could/should add more error checks on the calloc
calls, though):
#include <stdio.h>
#include <stdlib.h>
int increase(char*** data)
{
char** temp = realloc(*data, 2 * sizeof(char*));
if (temp != NULL) {
*data = temp; // Only overwrite the original if we succeed!
(*data)[1] = calloc(5, sizeof(char));
return 1;
}
else {
printf("Allocation error!"); // But we still have our original "data" pointer
return 0;
}
}
int main(void)
{
char** data = calloc(1, sizeof(char*));
data[0] = calloc(5, sizeof(char));
if (increase(&data)) {
data[1][3] = 'a';
printf("%c\n", data[1][3]); // Just to do some testing!
}
free(data); // "data" will be valid whether or not our call to "increase" works
return 0;
}
CodePudding user response:
- You do not have any 2D arrays in your code only array of pointer. Your question is about 2D arrays..
Trivial code (the number of columns does not change)
void *alloc2D(const size_t rows, const size_t cols, char (*arr)[cols])
{
arr = realloc(arr, rows * sizeof(*arr));
return arr;
}
Less trivial code - both are changing.
void *alloc2D(const size_t rows, const size_t cols, const size_t oldrows, const size_t oldcols, char (*arr)[oldcols])
{
char (*newarr)[cols] = malloc(rows * sizeof(*newarr));
if(arr && newarr)
{
for(size_t r = 0; r < rows && r < oldrows; r )
{
memcpy(newarr[r], arr[r], sizeof(*arr) > sizeof(*newarr) ? sizeof(*newarr) : sizeof(*arr));p
}
}
if(newarr) free(arr);
return newarr;
}
And some usage:
void *increase(const size_t oldrows, const size_t oldcols, char (*arr)[oldcols])
{
return alloc2D(oldrows 1, oldcols, arr);
}
int main(void)
{
size_t rows;
size_t cols;
if(scanf("%zu", &rows) != 1) { /* error handling*/}
if(scanf("%zu", &cols) != 1) { /* error handling*/}
char (*arr)[cols] = alloc2D(rows, cols, NULL);
char (*narr)[cols];
narr = increase(rows, cols, arr);
if(narr) arr = narr;
free(narr);
return 0;
}