Proper way to initialize a const member of dynamically allocated struct - c

I have these 2 structs:
struct Params {
int a;
int b;
};
struct Foo {
const struct Params settings;
int state;
};
The settings member is const as a hint that it should not be changed once a struct Foo has been created and initialized.
And I want to dynamically allocate this struct, e.g.
struct Foo * new_foo(void)
{
struct Foo *n = malloc(sizeof *n);
if (n) {
n->settings.a = SETTING_A;
n->settings.b = SETTING_B;
...
}
return n;
}
Now, this will not compile due to settings being const. What is a proper way to
initialize such a struct in this manner? Or is it better to not declare the settings member as const?

The memory is allocated (and thus, not constant), so it is legal to cast const away:
struct Foo * new_foo(void)
{
struct Foo *n = malloc(sizeof *n);
if (n) {
struct Params *s = (void *)&n->settings;
s->a = SETTING_A;
s->b = SETTING_B;
}
return n;
}

Here is one way to do it:
struct Foo *new_foo(void)
{
static struct Foo foo =
{
.settings =
{
.a = SETTING_A,
.b = SETTING_B
},
.state = ...
};
struct Foo *n = malloc(sizeof *n);
memcpy(n, &foo, sizeof *n);
return n;
}
Here is another way to do it:
struct Foo *new_foo(void)
{
static struct Params settings =
{
.a = SETTING_A,
.b = SETTING_B
};
struct Foo *n = malloc(sizeof *n);
memcpy((struct Params*)&n->settings, &settings, sizeof settings);
n->state = ...;
return n;
}

Related

How do I return one of two types in C?

Suppose this code
typedef struct A {
...
} A;
typedef struct B {
...
} B;
// If it was TypeScript I would say `type uknown = A | B;`
uknown getAorB(int k) {
if (k > 0) return (A){...};
return (B){...};
}
That function getAorB should return either A or B depending on the parameter k. OK, but what is the return type and is it possible to achieve that in C?
Use unions.
typedef struct A {
...
} A;
typedef struct B {
...
} B;
typedef union
{
struct A a;
struct B b;
}A_OR_B;
// If it was TypeScript I would say `type uknown = A | B;`
A_OR_B getAorB(int k) {
A_OR_B c;
if (k > 0) c.a.member = something;
else c.b.member = somethingelse;
return c;
}
One way to do this would be to have another struct which contains the 'type'
of the returned struct. Here is what this may look like:
#define STRUCTA 1
#define STRUCTB 2
typedef struct SUPER {
int type;
} SUPER;
typedef struct A {
int type;
...
} A;
typedef struct B {
int type;
...
} B;
SUPER* getAorB(int k) {
if (k > 0) {
A *a;
a = malloc(sizeof(*a));
a->type = STRUCTA;
return (SUPER*)a;
}
B *b;
b = malloc(sizeof(*b));
b->type = STRUCTB;
return (SUPER*)b;
}
Then in the calling function you check the type of the SUPER and cast it to the appropriate function.
A *a;
B *b;
if (returnedSuper->type == STRUCTA) {
a = (A*)returnedSuper;
}
else if (returnedSuper->type == STRUCTB) {
b = (B*)returnedSuper;
}

munmap_chunk(): invalid pointer while freeing a struct in an array

So I wrote a program where I have to realloc an array of structs whenever I want to add something to it.
But when I try to free the array, I free every element individually but I get a munmap_chunk(): invalid pointer at some point.
Here is the full code :
#include <stdlib.h>
#include <string.h>
struct Date {
int day;
int month;
int year;
};
struct Person {
char *name;
char *surname;
struct Date birth;
};
struct Directory {
int size;
struct Person *array;
};
struct Date create_date() {
struct Date date = {
.day = 0,
.month = 0,
.year = 0
};
return date;
}
struct Directory create_directory() {
struct Directory directory = {
.size = 0,
.array = NULL
};
return directory;
}
struct Person *create_person() {
struct Person *person_ptr = (struct Person *) malloc(sizeof(struct Person));
person_ptr->name = NULL;
person_ptr->surname = NULL;
return person_ptr;
}
void copy_date(struct Date *dest, struct Date *src) {
dest->day = src->day;
dest->month = src->month;
dest->year = src->year;
}
void initialize_person(struct Person *person_ptr, char *name, char *surname, struct Date *birth) {
if (name != NULL && surname != NULL && birth != NULL) {
person_ptr->name = realloc((*person_ptr).name, (strlen(name) * sizeof(char)) + 1);
strcpy(person_ptr->name, name);
person_ptr->surname = realloc((*person_ptr).surname, (strlen(surname) * sizeof(char)) + 1);
strcpy(person_ptr->surname, surname);
copy_date(&person_ptr->birth, birth);
}
}
void copy_person(struct Person *dest, struct Person *src) {
dest->name = realloc((*dest).name, (strlen(src->name) * sizeof(char)) + 1);
dest->surname = realloc((*dest).surname, (strlen(src->surname) * sizeof(char)) + 1);
struct Date date = create_date();
dest->birth = date;
strcpy(dest->name, src->name);
strcpy(dest->surname, src->surname);
copy_date(&dest->birth, &src->birth);
}
int add_person(struct Directory *directory_ptr, const struct Person *new_person_ptr) {
int return_code = 0;
directory_ptr->size++;
directory_ptr->array = realloc(directory_ptr->array, (directory_ptr->size * sizeof(struct Person)));
if (directory_ptr->array) {
copy_person(&directory_ptr->array[directory_ptr->size - 1], (struct Person *) new_person_ptr);
} else {
return_code = 1;
}
return return_code;
}
int add_multiple_persons(struct Directory *directory_ptr, const struct Person **persons_ptr, int nb_persons) {
for (int i = 0; i < nb_persons; i++) {
add_person(directory_ptr, (persons_ptr[i]));
}
return 0;
}
void destroy_person(struct Person *person_ptr) {
free(person_ptr->name);
person_ptr->name = NULL;
free(person_ptr->surname);
person_ptr->surname = NULL;
free(person_ptr);
person_ptr = NULL;
}
void destroy_directory(struct Directory *directory_ptr) {
if (directory_ptr->array) {
for (int i = 0; i < directory_ptr->size; i++) {
destroy_person(&directory_ptr->array[i]);
}
directory_ptr->array = NULL;
directory_ptr->size = 0;
}
}
int main(void) {
struct Directory directory = create_directory();
struct Person *person1 = create_person();
struct Person *person2 = create_person();
struct Person *person3 = create_person();
struct Date date = {
.day = 17,
.month = 04,
.year = 1999};
initialize_person(person1, "Marcel", "Juan", &date);
initialize_person(person2, "Albin", "Michel", &date);
initialize_person(person3, "Suzerain", "Bernard", &date);
const struct Person *array[] = {
person1,
person2,
person3
};
add_multiple_persons(&directory, array, 3);
destroy_person(person1);
destroy_person(person2);
destroy_person(person3);
destroy_directory(&directory);
return 0;
}
I've been on this error for more than a week, and it keeps bugging me.
How can I fix this ?
In the destroy_directory function, you freed the persons contained by the array. But in this array you didn't put pointers to structures but the structures themselves. Therefore you must free the space you allocated for the array and nothing else :
void destroy_directory(struct Directory *directory_ptr) {
if (directory_ptr->array) {
free(directory_ptr->array); //<==== Here
directory_ptr->array = NULL;
directory_ptr->size = 0;
}
}
person_ptr is a part of the memory allocated at directory_ptr->array. You need to remove this line.
As a rule of gold, memory responsible is the same while allocation and while freeing. In your code, the person holder is the array inside directory_ptr, which is allocated by add_person. Despite its name, it is a directory manager, so freeing its memory should be done only on directory destroyer.

Warning from gcc: braces around scalar initializer, how to fix?

At rewriting kernel driver I got this warning:
msm-cirrus-playback.c:545:2: warning: braces around scalar initializer
Read that this warning appears when I am declaring one structure's field in {}:
struct random_struct test = {
{ .name = "StackOverflow" },
{ .name = "StackExchange" },
};
But my structure have 2-3 fields in {}:
static struct device_attribute *opalum_dev_attr = {
{
.attr->name = "temp-acc",
.show = opsl_temp_acc_show,
.store = opsl_temp_acc_store,
},
{
.attr->name = "count",
.show = opsl_count_show,
.store = opsl_count_store,
},
{
.attr->name = "ambient",
.show = opsl_ambient_show,
.store = opsl_ambient_store,
},
{
.attr->name = "f0",
.show = opsl_f0_show,
.store = opsl_f0_store,
},
{
.attr->name = "pass",
.show = opsl_pass_show,
},
{
.attr->name = "start",
.show = opsl_cali_start,
},
};
This structure:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
How can I fix this warning? Qualcomm kernels are building with -Werror flag, so this warning is critical.
static struct device_attribute *opalum_dev_attr means declare opalum_dev_attr as static pointer to struct device_attribute
Your code is trying to initialize a static array of struct device_attribute
What you want is: static struct device_attribute opalum_dev_attr[] which means declare opalum_dev_attr as static array of struct device_attribute
It is because you initialize the pointer to the struct not the struct itself.
you need to assign the reference to the struct for example by using compound literals
struct x
{
int a,b,c;
}a = {1,2,3};
void foo()
{
struct x a = {1,2,3};
struct x *b = {1,2,3}; // wrong warning here
struct x *x = &(struct x){1,2,3}; // correct reference to the struct assigned (using compound literal
struct x *y = (struct x[]){{1,2,3}, {4,5,6}, {4,5,6}, };
struct x z[] = {{1,2,3}, {4,5,6}, {4,5,6}, };
}

Is it possible in C to have a struct or union of functions?

Is there any way, whether union, struct, or something else, to have a group of functions?
typedef struct {
//ERROR
int sqr(int i) {
return i * i;
}
//ERROR
int cube (int i) {
return i * i * i;
}
} test;
Fields in structs can be function pointers:
struct Interface {
int (*eval)(int i);
};
You cannot define the functions in the struct body, but you can assign functions with the same signature to the struct fields:
int my_sqr(int i) {
return i * i;
}
int my_cube(int i) {
return i * i * i;
}
struct Interface squarer = { my_sqr };
struct Interface cuber = { my_cube };
Then call the fields like a normal function:
printf("%d\n", squarer.eval(4)); // "16"
printf("%d\n", cuber.eval(4)); // "64"

Passing a value with struct type into a function in C

typedef struct {
nat id;
char *data;
} element_struct;
typedef element_struct * element;
void push(element e, queue s) {
nat lt = s->length;
if (lt == max_length - 1) {
printf("Error in push: Queue is full.\n");
return;
}
else {
s->contents[lt] = e;
s->length = lt + 1;
}
}
int main () {
push(something_of_type_element, s);
}
How would i go about formatting "something_of_type_element"?
Thanks
Notes:
nat is the same as int
How about:
element elem = malloc(sizeof(element_struct));
if (elem == NULL) {
/* Handle error. */
}
elem->id = something;
elem->data = something_else;
push(elem, s);
Note that there's lots of memory management missing here...
Like this:
element_struct foo = { 1, "bar" };
push(&foo, s);
If you have a C99 compiler you can do this:
element_struct foo = {
.id = 1,
.data = "bar"
};
push(&foo, s);
Note that the data in the structure must be copied if it needs to live longer than the scope in which it was defined. Otherwise, memory can be allocated on the heap with malloc (see below), or a global or static variable could be used.
element_struct foo = malloc(sizeof (element_struct));
foo.id = 1;
foo.data = "bar";
push(foo, s);

Resources