How to correctly use flexible array member? - c

Here is a type I declared:
(I declared t_sphere, t_cylinder and t_triangle too)
typedef struct s_intersection{
double t1;
double t2;
int id;
union {
t_sphere sph;
t_cylinder cyl;
t_triangle tri;
} u[];
} t_intersection;
To refer to the union variable inside my struct, I have to know in advance what type it is. This is possible thanks to the id int declared in my struct, but it is very heavy to achieve with the method I thought of.
Here is the function I wrote to allocate a t_intersection variable:
t_intersection *intersection(unsigned int geometric_figure_id, void *figure)
{
t_intersection *p;
if (geometric_figure_id == SPHERE_ID)
p = malloc(sizeof(*p) + sizeof (p->u[0].sp));
else if (geometric_figure_id == CYLINDER_ID)
p = malloc(sizeof(*p) + sizeof (p->u[0].cy));
else if (geometric_figure_id == TRIANGLE_ID)
p = malloc(sizeof(*p) + sizeof (p->u[0].tr));
if (p == NULL)
return (NULL);
memset(&p, 0, sizeof(p));
p->id = geometric_figure_id:
if (geometric_figure_id == SPHERE_ID)
p->u[0].sp = *(t_sphere *)figure;
else if (geometric_figure_id == CYLINDER_ID)
p->u[0].cy = *(t_cylinder *)figure;
else if (geometric_figure_id == TRIANGLE_ID)
p->u[0].tr = *(t_triangle *)figure;
return (p);
}
Is there a better way of achieving this?
EDIT: I added the mallocs I forgot + assigning the id...

You need to always allocate space for the entire union, not just one member. Also, you can use calloc to allocate memory that is zero-initialized.
So you can replace this:
if (geometric_figure_id == SPHERE_ID)
p = malloc(sizeof(*p) + sizeof (p->u[0].sp));
else if (geometric_figure_id == CYLINDER_ID)
p = malloc(sizeof(*p) + sizeof (p->u[0].cy));
else if (geometric_figure_id == TRIANGLE_ID)
p = malloc(sizeof(*p) + sizeof (p->u[0].tr));
if (p == NULL)
return (NULL);
memset(&p, 0, sizeof(p));
With this:
p = calloc(1, sizeof(*p) + sizeof (p->u[0]));
if (p == NULL)
return (NULL);
To simplify even more, since you're only every creating one instance of the union, you don't need a flexible array member. Just declare your struct like this:
typedef struct s_intersection{
double t1;
double t2;
int id;
union {
t_sphere sph;
t_cylinder cyl;
t_triangle tri;
};
} t_intersection;
And create an instance like this:
t_intersection *intersection(unsigned int geometric_figure_id, void *figure)
{
t_intersection *p;
p = calloc(1, sizeof(*p));
if (p == NULL)
return (NULL);
p->id = geometric_figure_id:
if (geometric_figure_id == SPHERE_ID)
p->sp = *(t_sphere *)figure;
else if (geometric_figure_id == CYLINDER_ID)
p->cy = *(t_cylinder *)figure;
else if (geometric_figure_id == TRIANGLE_ID)
p->tr = *(t_triangle *)figure;
return (p);
}

Bug
memset(&p, 0, sizeof(p)); overwrote the pointer just returned form malloc(). #M.M
Likely wanted memset(p, 0, sizeof p[0]);
Memory usage
You can follow #dbush approach and allocate to the size of the FAM union in all cases.
When the .u member's type is not changed over the life of p, code can be more memory efficient.
OP's original approach reduces over-allocating yet could use some improvements.
t_intersection *intersection(unsigned geometric_figure_id, const void *figure) {
assert(geometric_figure_id < ID_N);
static const size_t fig_size[ID_N] = {
[SPHERE_ID] = sizeof(t_sphere),
[CYLINDER_ID] = sizeof(t_cylinder),
[TRIANGLE_ID] = sizeof(t_triangle),
};
t_intersection *p = malloc(sizeof p[0] + fig_size[geometric_figure_id]);
if (p) {
memset(p, 0, sizeof p[0]);
memcpy(p->u, figure, fig_size[geometric_figure_id]);
}
return p;
}
Advanced
There is another efficiency step discussed here that uses offsetof(p, u) instead of sizeof p[0].

Related

What's wrong with my realloc doing on 2d-array

I am solving binary tree paths leet code programming question 257. I am having issue for one of the larger input where my code is getting segmentation fault. I suspect that there is an problem with my realloc but I am not able to figure it out.
Below is my approach:
Initially I started by dynamically allocating 80 bytes of memory of type char (80/8 = 10 rows)and storing the returned address to char **res variable.
char ** res = (char **)malloc(sizeof(char *) * sum);
I am calling findpath function recursively to find all the binary tree paths. Whenever one path is found , I dynamic allocate 100 bytes for each row index.
res[resIdx] = (char *)malloc(sizeof(char) * 100);
I have one global variable resIdx which points to the current row index where I copy the found binary tree path and increment the global variable resIdx.
if the resIdx becomes greater then total number of rows which was previously allocated then I do realloc of the memory but it looks like realloc is getting failed.
if (resIdx >= sum)
{
sum = sum + 10;
res = (char **)realloc(res,sizeof(char *) * sum); //Any issue here?
}
Can anyone please help me to figure out what's wrong I am doing in my code. Below is my full code
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int sum;
int resIdx;
void findpath (struct TreeNode* root, int *ls,int ls_idx,char **res);
char ** binaryTreePaths(struct TreeNode* root, int* returnSize){
if (root == NULL)
{
*returnSize = 0;
return NULL;
}
resIdx = 0;
sum = 10;
char ** res = (char **)malloc(sizeof(char *) * sum);
int ls[100];
findpath(root,&ls[0],0,res);
*returnSize = resIdx;
return &res[0];
}
void findpath (struct TreeNode* root, int *ls,int ls_idx,char **res)
{
char temp[100];
int l=0,i=0;
if (root->left == NULL && root->right == NULL)
{
ls[ls_idx] = root->val;
ls_idx+=1;
if (resIdx >= sum)
{
sum = sum + 10;
res = (char **)realloc(res,sizeof(char *) * sum);
}
res[resIdx] = (char *)malloc(sizeof(char) * 100);
while (i < ls_idx)
{
if (i==0)
{
l = l + sprintf(&temp[l], "%d", ls[i]);
}
else
{
l = l + sprintf(&temp[l], "->%d", ls[i]);
}
i++;
}
strcpy(res[resIdx],temp);
resIdx++;
return;
}
ls[ls_idx] = root->val;
if (root->left != NULL)
{
findpath(root->left,ls,ls_idx+1,res);
}
if (root->right != NULL)
{
findpath(root->right,ls,ls_idx+1,res);
}
return;
}
The last argument to your findPath function is declared as a char** type; thus, when you make the call findpath(root,&ls[0],0,res); in binaryTreePaths, where the res variable is a char** type, a copy of that pointer is passed to the findPath function (most likely, but not necessarily, by placing that copy on the stack).
Then, if reallocation is required, the res = (char **)realloc(res,sizeof(char *) * sum); line in that function overwrites the value in the passed copy and, at the same time (if the call is successful – vide infra), will (probably) invalidate (i.e. free) the memory referenced by the previous address in that res copy. Thus, when control returns to the calling binaryTreePaths function, its own version of res will not have been modified and will remain pointing to that (now invalid) memory.
So, in order for your findPath function to be able to modify the given res argument, that must be passed as a pointer – in this case, a pointer to a char**, which will be of type char***; then, when called, you will need to pass the address of the res variable in binaryTreePaths.
Note also that directly overwriting a pointer in a call to realloc, as you have done in the line of code quoted above is dangerous. This is because, should that call fail, then you have lost the original data pointer (it will have been overwritten with NULL) and error recovery will be very difficult. You should save the return value in a temporary variable and only replace your original if the call succeeds.
With the code you have provided, I cannot properly test for any other errors but, taking the points above in hand, the below is a possible fix. See also: Do I cast the result of malloc?
int sum;
int resIdx;
void findpath(struct TreeNode* root, int* ls, int ls_idx, char*** res); // Note last argument type!
char** binaryTreePaths(struct TreeNode* root, int* returnSize)
{
if (root == NULL) {
*returnSize = 0;
return NULL;
}
resIdx = 0;
sum = 10;
char** res = malloc(sizeof(char*) * sum);
int ls[100];
findpath(root, &ls[0], 0, &res); // Pass ADDRESS of res
*returnSize = resIdx;
return &res[0];
}
void findpath(struct TreeNode* root, int* ls, int ls_idx, char*** res)
{
char temp[100];
int l = 0, i = 0;
if (root->left == NULL && root->right == NULL) {
ls[ls_idx] = root->val;
ls_idx += 1;
if (resIdx >= sum) {
sum = sum + 10;
char** test = realloc(*res, sizeof(char*) * sum);
if (test == NULL) {
// Handle/signal error
return;
}
*res = test; // Only replace original if realloc succeeded!
}
(*res)[resIdx] = malloc(sizeof(char) * 100);
while (i < ls_idx) {
if (i == 0) {
l = l + sprintf(&temp[l], "%d", ls[i]);
}
else {
l = l + sprintf(&temp[l], "->%d", ls[i]);
}
i++;
}
strcpy((*res)[resIdx], temp);
resIdx++;
return;
}
ls[ls_idx] = root->val;
if (root->left != NULL) {
findpath(root->left, ls, ls_idx + 1, res);
}
if (root->right != NULL) {
findpath(root->right, ls, ls_idx + 1, res);
}
return;
}

realloc: invalid checksum for freed object

I have an error using realloc to replace malloc.
This code below runs OK on my computer.
int vector_grow(Vector* vec) {
unsigned long newcap;
int * newarr;
if (0 == vec->cap) {
vec->arr = (int*)malloc(START_CAPACITY * sizeof(*vec->arr));
if (NULL == vec->arr)
return -1;
vec->cap = START_CAPACITY;
return 0;
}
newarr = malloc (newcap * sizeof(*vec->arr));
if (NULL == newarr)
return -1;
memcpy (newarr, vec->arr, vec->len * sizeof(*vec->arr));
free (vec->arr);
vec->arr = newarr;
vec->cap = newcap;
return 0;
}
I want to change the malloc to realloc, but the error occurs.
int vector_grow(Vector* vec) {
unsigned long newcap;
if (0 == vec->cap) {
vec->arr = (int*)malloc(START_CAPACITY * sizeof(*vec->arr));
if (NULL == vec->arr)
return -1;
vec->cap = START_CAPACITY;
return 0;
}
newcap = 2 * vec->cap;
if ((vec->arr = (int*)realloc(vec->arr, newcap * sizeof(int))) == NULL)
return -1;
return 0;
}
It says
malloc: *** error for object 0x7fca64c02598: incorrect checksum for freed object - object was probably modified after being freed.
I don't know any difference between those two snippets of code, if you know what causes the error, please tell me! Thank you very much!
Bug in missing vec->cap = in updated code certainly contribute to various calls to malloc() and calling code's misuse of data.
int vector_grow(Vector* vec) {
unsigned long newcap;
if (0 == vec->cap) {
... // not important to show the bug
}
newcap = 2 * vec->cap;
if ((vec->arr = (int*)realloc(vec->arr, newcap * sizeof(int))) == NULL)
return -1;
// Add missing update
vec->cap = newcap;
return 0;
}
Also better to test for allocation success
void *p = realloc(vec->arr, sizeof *(vec->arr) * newcap);
if (p == NULL) {
return -1;
}
vec->arr = p;
vec->cap = newcap;
The only scenario where I can imagine such error message is when you actually modify the pointer, for example
int *x = malloc(2 * sizeof *x);
if (x != NULL) {
x = x + 1;
free(x);
}
The pointer that MUST be passed to free() MUST had been returned by malloc()/calloc()/realloc(), passing any other pointer including a pointer to the same data but at a different position like x in the example above is undefined behavior.

Initializing pointers in a struct in a minimum of source lines

I'm currently new to C programming, and appreciate for any tip.
Is there a shorter way to initialize struct pointers in C without removing the pointer tags?
typedef struct {
int x, y, z;
} Point3;
typedef struct {
Point3 *pos, *direction;
} Vector;
int main() {
Vector *p;
p = malloc(sizeof(Vector));
p->pos = malloc(sizeof(Point3));
p->direction = malloc(sizeof(Point3));
return 0;
}
Yes, there is a shorter way — one which is one malloc() call shorter.
Vector *p = malloc(sizeof(Vector));
if (p != 0)
{
p->pos = malloc(2 * sizeof(Point3));
if (p->pos != 0)
p->direction = &p->pos[1];
}
Allocate an array of 2 Point3 values. p->pos points to the first, and p->direction points to the second (or vice versa).
It is still 3 statements (plus error checking) and two calls to malloc(), though.
In practice, you could almost certainly get away with:
Vector *p = malloc(sizeof(Vector) + 2 * sizeof(Point3));
if (p != 0)
{
p->pos = (void *)((char *)p + sizeof(Vector));
p->direction = (void *)((char *)p + sizeof(Vector) + sizeof(Point3));
}
I am not sure that is sanctioned by the C standard, but I can't immediately think of a plausible platform configuration where it would actually fail to work correctly. It would fail if you found some bizarre platform where addresses were 16-bits each but int was 8 bytes and had to be 8-byte aligned, but that's hardly plausible.
To me, it makes far more sense to put the Point3 members directly in the Vector, instead of pointers. Fewer allocations, less memory fragmentation, fewer de-references, fewer cache-misses.
typedef struct {
int x, y, z;
} Point3;
typedef struct {
Point3 pos, direction;
} Vector;
int main(void) {
/* Local (stack) allocation of a Vector, initialized to all zeros */
Vector v = {};
/* Dynamic (heap) allocation of a Vector, initialized to all zeros */
Vector *p;
p = malloc(sizeof(Vector));
if (!p) {
return 1; // failure
}
*p = (Vector){};
return 0;
}
Unfortunately, there is no other way. You can simplify memory allocation with another function, like this
Vector* allocate_vector( ) {
Vector* v = (Vector*)malloc( sizeof(Vector) );
if( v == NULL ) {
/**/
}
v->pos = (Point3*)malloc( sizeof(Point3) );
if( v->pos == NULL ) {
/**/
}
v->direction = (Point3*)malloc( sizeof(Point3) );
if( v->direction == NULL ) {
/**/
}
return v;
}
And then use it, when you need new Vector.
Vector* v = allocate_vector( );

Segfault when returning an int value;

I know this question might sound quite stupid, but I've tried my best and I can't seem to have solved issues with this code:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay = strsplit(code, ";");
char* a = *yay;
while (a != NULL) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
*(yay)++;
a = *yay;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
It segfaults when returning, since it the printf is executed alright., and another printf call /after/ the function is called (on int main()) doesn't print anything at all.
I'm sorry if this sounds too specific, but I don't know where else to get help.
Apparently you are corrupting the stack somehow.
your line does not initialize the pointer:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
but you write to it like this, which will have undefined behavior:
statms[sta] = statm;
Easiest way to fix is probably to restore the malloc (provided the size is correct)
struct bacon_statement* statms = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
Seems like a stack overrun, you keep assigning data to the buffer (statms) without checking for boundaries. Sooner or later, the return address (which is on the stack) will be overridden and when return is reached, the address to return to will be corrupted. and there is your segmentation fault.
Try doing something like:
int count = 0;
char *ptr = strchr(code, ';');
while (ptr) {
count++;
ptr = strchr(ptr + 1, ';');
}
to predict the number of statements, and then:
struct bacon_statement* statms = (struct bacon_statement*) malloc(
count * sizeof(struct bacon_statement));
to allocate enough bacon_statement slots. Alternatives are probably more difficult: a linked list or other mutable structure; or using realloc to increase the size of the array, keeping a note of how many sots you have left.
The mistake can still be in the other functions though!
The code does too much. Let me summarise:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay = strsplit(code, ";");
char* a;
while ((a = *yay)) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
*(yay)++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
But we can get more compact than that:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay;;
char* a;
for (yay = strsplit(code, ";"); (a = *yay); *(yay)++ ) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
Now let's remove the silly stringcompare:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay;
char* a;
for (yay = strsplit(code, ";"); (a = *yay); *(yay)++ ) {
puts(a);
if (*a != '#') {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
Still does not make sense. IMO the OP wants to loop trough an array of string-pointers (yay) and process each string in it. Especially the *(yay)++ looks awkward.
Maybe with the '#' he wants to skip comments. I'd expect something like:
sta=0;
for (yay = strsplit(code, ";"); (a = *yay); yay++ ) {
int err;
if (*a == '#') continue;
/* make bacon from a */
err = bacon_make_statements(a, statms[sta] );
if (err) return err;
sta++; /* could overflow ... */
}
/* you need the number of assigned struct members ("sta") to this function */
return bacon_internal_make(statms,sta internal);
On second thought, my guess is that the strsplit() function return a pointer to an automatic ("stack") variable. Or the *yay variable is incremented beyond recognition. Or the statms[] array is indexed out of bounds.
Is it possible that you meant to use ind instead of sta as the array index variable and sta is uninitialized?

Realloc is not resizing array of pointers

I keep passing in and returning the dirs_later_array. When I get to "new_size=..." in the else block, I end up with new_size of 2 the second time around. So far so good. But when I do a realloc
dirs_later_array = realloc(dirs_later_array,
new_size * sizeof(struct dirs_later*));
the sizeof remains at 4, the size of the pointer, for dirs_later_array. I'm able to succesfully store at dirs_later_array[1] but that value keeps getting overwritten the next time I go into the function.
struct dirs_later** add_struct(const char *findme, struct dirent *dptr,
struct stat *this_lstat, char *relative_path, const char *type_str,
struct dirs_later **dirs_later_array) {
struct dirs_later *new_dir = malloc(sizeof(struct dirs_later));
check_realloc_dirs_error(new_dir);
if (strcmp(dptr->d_name, ".")) { //Dir and not same directory
//Copy the relative path to the struct
char *relative_path2;
relative_path2 = malloc(strlen(relative_path) + 1);
check_realloc_error(relative_path2);
strcpy(relative_path2, relative_path);
//if (strlen(relative_path) > 0)
// relative_path2[strlen(relative_path) - 1] = '\0';
if (NULL != new_dir) {
new_dir->findme = findme;
new_dir->dptr = dptr;
new_dir->st_mode = this_lstat->st_mode;
new_dir->relative_path = relative_path2;
new_dir->type_str = type_str;
}
int new_size = 0;
/*
//Check if this is the first element in the struct
if (sizeof(dirs_later_array) / sizeof(struct dirs_later*) == 1) {
new_size = 1;
}
*/
if (dirs_later_array == NULL) {
dirs_later_array = malloc(sizeof(struct dirs_later*)); //Store the directory structures or process later
check_realloc_arr_error(*dirs_later_array);
new_size = 1;
} else {
//Add directories to directories array
new_size = (((sizeof(dirs_later_array) + sizeof(struct dirs_later*)))/sizeof(struct dirs_later*));
//printf("new size: %d",new_size);
}
dirs_later_array = realloc(dirs_later_array,
new_size * sizeof(struct dirs_later*));
check_realloc_arr_error(dirs_later_array);
dirs_later_array[new_size - 1] = new_dir;
}
return dirs_later_array;
}
Operator sizeof is a compile time feature and it only checks the static size of an expression. So for pointer it only returns the size of that pointer which is 4 on your platform. sizeof does not measure the size of a dynamically allocated data. There is no standard feature in C to get the size of dynamically allocated data.
Your sizeof(struct dirs_later*) should be changed to sizeof(struct dirs_later) - as before!
Also the sizeof is a compile time feature. You need a structure like this to hold the size
struct my_dirs
struct dirs_later *dirs;
int size;
};
Initialise it like this
struct my_dirs directories;
directories.size = 0;
directories.dirs = NULL;
Then to add (note realloc can take NULL as a parameter
directories.dirs = realloc(directories.dirs,
(++directories.size) * sizeof(struct dirs_later));
This would also simplify your code.

Resources