How can I pass a generic struct to a function in C? - c

I'm beginner in C programming and I have a doubt about to pass a generic struct to a function in C.
Here is what I have:
typedef struct {
char name[20];
float price;
} product;
typedef struct {
char name[20];
int type;
} category;
And I want to do something like this:
void changeName(struct *s, newName[20]) {
strcpy(s->name, newName);
}
If someone has already asked that, please disconsider this and sends me the issue link.
Someone can help me?
Thanks.

Using a union
One approach would be to add a structure containing a union, itself containing pointers to product and category structures, as well as an enum to identify the type of data in the struct. This union, or a pointer to it, could be passed to a change_name() function.
Here is an example that would work in C11. It uses an unnamed union member, so this is not valid C99 code:
#include <stdio.h>
#include <string.h>
typedef struct {
char name[20];
float price;
} product;
typedef struct {
char name[20];
int type;
} category;
typedef struct {
enum { PRODUCT, CATEGORY } type;
union {
product *prod;
category *cat;
};
} generic;
void change_name(generic *gen, const char *new_name);
int main(void)
{
product prod_a = { .name = "widget", .price = 1.99 };
category cat_a = { .name = "general", .type = 1 };
generic gen_prod_a = { .type = PRODUCT, .prod = &prod_a };
generic gen_cat_a = { .type = CATEGORY, .cat = &cat_a };
printf("prod_a.name = %s\n", prod_a.name);
printf("cat_a.name = %s\n", cat_a.name);
change_name(&gen_prod_a, "gadget");
change_name(&gen_cat_a, "specific");
printf("prod_a.name = %s\n", prod_a.name);
printf("cat_a.name = %s\n", cat_a.name);
return 0;
}
void change_name(generic *gen, const char *new_name)
{
switch (gen->type) {
case PRODUCT:
strcpy(gen->prod->name, new_name);
break;
case CATEGORY:
strcpy(gen->cat->name, new_name);
break;
default:
fprintf(stderr, "Unknown type in change_name()\n");
}
}
This could be made to work in C99 by naming the union:
typedef struct {
enum { PRODUCT, CATEGORY } type;
union {
product *prod;
category *cat;
} data; // named for C99
} generic;
/* ... */
generic gen_prod_a = { .type = PRODUCT, .data.prod = &prod_a };
generic gen_cat_a = { .type = CATEGORY, .data.cat = &cat_a };
/* ... */
void change_name(generic *gen, const char *new_name)
{
switch (gen->type) {
case PRODUCT:
strcpy(gen->data.prod->name, new_name);
break;
case CATEGORY:
strcpy(gen->data.cat->name, new_name);
break;
default:
fprintf(stderr, "Unknown type in change_name()\n");
}
}
Alternatively, one struct type could hold an enum identifier and a union containing the product and category structures. This approach may seem a bit more streamlined:
#include <stdio.h>
#include <string.h>
typedef struct {
enum { PRODUCT, CATEGORY } type;
union {
struct {
char name[20];
float price;
} prod;
struct {
char name[20];
int type;
} cat;
} data;
} record;
void change_name(record *rec, const char *new_name);
int main(void)
{
record prod_a = { .type = PRODUCT };
change_name(&prod_a, "widget");
prod_a.data.prod.price = 1.99;
record cat_a = { .type = CATEGORY };
change_name(&cat_a, "general");
cat_a.data.cat.type = 1;
printf("prod_a.name = %s\n", prod_a.data.prod.name);
printf("cat_a.name = %s\n", cat_a.data.cat.name);
change_name(&prod_a, "gadget");
change_name(&cat_a, "specific");
printf("prod_a.name = %s\n", prod_a.data.prod.name);
printf("cat_a.name = %s\n", cat_a.data.cat.name);
return 0;
}
void change_name(record *rec, const char *new_name)
{
switch (rec->type) {
case PRODUCT:
strcpy(rec->data.prod.name, new_name);
break;
case CATEGORY:
strcpy(rec->data.cat.name, new_name);
break;
default:
fprintf(stderr, "Unknown type in change_name()\n");
}
}
Using a type-generic macro
Both of the above approaches are a little bit awkward. Another solution, available for C11 only, is to use the _Generic keyword in a type-generic macro. Here, functions are written for each expected data type, and a macro selects which function definition is to be used based on type. A virtue of this approach is that as new types are added, only new functions and updates to the type-generic macro are needed to handle them.
#include <stdio.h>
#include <string.h>
#define change_name(S, N) _Generic ((S), \
prod_ptr: change_name_prod, \
cat_ptr: change_name_cat \
)((S), (N))
typedef struct {
char name[20];
float price;
} product;
typedef struct {
char name[20];
int type;
} category;
typedef product *prod_ptr;
typedef category *cat_ptr;
void change_name_prod(product *prod, const char *new_name);
void change_name_cat(category *cat, const char *new_name);
int main(void)
{
product prod_a = { .name = "widget", .price = 1.99 };
category cat_a = { .name = "general", .type = 1 };
printf("prod_a.name = %s\n", prod_a.name);
printf("cat_a.name = %s\n", cat_a.name);
change_name(&prod_a, "gadget");
change_name(&cat_a, "specific");
printf("prod_a.name = %s\n", prod_a.name);
printf("cat_a.name = %s\n", cat_a.name);
return 0;
}
void change_name_prod(product *prod, const char *new_name)
{
strcpy(prod->name, new_name);
}
void change_name_cat(category *cat, const char *new_name)
{
strcpy(cat->name, new_name);
}
All of the above programs have the same output:
prod_a.name = widget
cat_a.name = general
prod_a.name = gadget
cat_a.name = specific

You have already typedef-ed the structures. You can use these other names.
For example, if the name is product, first create a variable and call the function
product var={"name", 1.2};
changeName(&var, "AnotherName");
Then pass this variable to a function
void changeName(product *s, char newName[])
{
strcpy(s->name, newName);
}

Generic programming, which implies type-safety just doesn't really exist in C. But there are ways around that.
What I understand from your question, is 'Can I define a function that can be applied to common elements of different structures?
Let's expand you example a bit, to have items which share a common trait.
struct info
{
char name[20];
int id;
};
struct product
{
Info info;
int price;
};
struct category
{
Info info;
int type;
};
You can now define a function that would work safely on the traits shared by both product and category.
void changeName(info* p, const char* name)
{
strcpy_s(info->name, sizeof(info->name), name);
}
int main()
{
category cat;
product prod;
memset(&cat, 0, sizeof(cat));
memset(&prod, 0, sizeof(prod));
changeName(&cat.info, "Category 1");
changeName(&prod.info, "blue product");
return 0;
}

Related

Pointers to a struct inside a struct

I have two structures (one_d and two_d).
I have a function that will take struct two_d *smg as input. In main(), I am trying to create such smg so it will return value c increased.
My problem is that, while creating an array of struct two_d smg[2], I am not sure how to put inside information about its values, as it is a pointer to a different struct.
So how do you use pointer to a struct inside a struct? I would like to create struct two_d smg[2] but i dont now how to deal with struct one_d *a field in it
#include <stdio.h>
enum sid
{
DRB,
DRA,
};
struct one_d
{
unsigned int r;
unsigned int *p;
};
struct two_d
{
struct one_d *a;
enum sid z;
};
unsigned int getSmg(struct two_d *smg)
{
unsigned int c = 0;
const struct two_d *sd = NULL;
const struct one_d *ed = NULL;
for (sd = smg; sd->a != NULL; ++sd)
{
for (ed = sd->a; ed->p != NULL; ++ed)
{
if (DRA == sd->z)
{
/*P Increment the clear-state buffer size */
c += 1 + ed->r;
}
}
}
return c;
}
int main(void)
{
unsigned int rVal = 0;
struct two_d smg[2]={
//
// [0].a ={1,0},
// [0].z =DRA,
// [1].a={1,0},
// [1].z =DRA,
};
rVal = getSmg(smg);
printf("Return value is a %d\n", rVal);
printf("Return value is a l");
return( 0 );
}
Well, at least this compiles... I'm not game to run it, though...
For what it's worth...
enum sid { DRB, DRA, DRAwhoCares };
typedef struct {
unsigned int r;
unsigned int *p;
} oneD_t;
typedef struct {
oneD_t *a;
enum sid z;
} twoD_t;
unsigned int getSmg( twoD_t *smg ) {
unsigned int c = 0;
for( twoD_t *sd = smg; sd->a != NULL; +sd++ ) {
for( oneD_t *ed = sd->a; ed->p != NULL; ed++ ) {
if( DRA == sd->z ) {
/*P Increment the clear-state buffer size */
c += 1 + ed->r;
}
}
}
return c;
}
int main( void ) {
oneD_t foo[] = { { 1, NULL }, /* ... */ };
oneD_t bar[] = { { 1, NULL }, /* ... */ };
twoD_t smg[]={
{ foo, DRA, },
{ bar, DRA, },
{ NULL, DRAwhoCares, },
};
unsigned int rVal = getSmg( smg );
printf( "Return value: %u\n", rVal );
return 0; // return is not a function call... No parenthesis...
}

Struct Initialization differnece

Given a struct in C defined as follows:
struct Person {
const char *name;
int age;
}
What are the differences between the two declarations below? I was confused when the struct keyword would precede the initialization below:
int main() {
struct Person John = { .name = "John", .age = 10 };
Person Jane = { .name = "Jane", .age = 10 };
return 0;
}
If you define the struct in this way:
struct Person {
const char *name;
int age;
}
Then (1) compiles while (2) does not, since the type of the struct is struct Person, not Person. "struct" is required.
// (1)
struct Person John = { .name = "John", .age = 10 };
// (2)
Person Jane = { .name = "Jane", .age = 10 };
However, if you use typedef:
typedef struct person_t {
const char *name;
int age;
} Person;
Then you can use both struct person_t and Person as the latter is an alias of the former.

How to get the value of enum from a string

code is here!
I tried to get the value of enum value as string from the user input and want to decode the value and print the case according to it, using Switch case but can't decode the exact value.
enum design {E2F = 1, E2, E3, E4, E5}; char *designation[5];
If someone helps I will be happy
Thanks.
An enum maps symbols to numbers. Here are the 3 options we discussed:
If you want to map strings to numbers use a struct:
struct {
const char *design;
int value;
} designs[] = {
{"E2F", 1},
{"E2", 2},
{"E3", 3}
{"E4", 4},
{"E5", 5}
};
If you want the struct defined in terms of the enum. Generate both from the same data (DESIGNS):
#define DESIGNS\
_(E2F, 1)\
_(E2, 2)\
_(E3, 3)\
_(E4, 4)\
_(E5, 5)
#define _(A, B) A = B,
enum {
DESIGNS
};
#undef _
#define _(A, B) { #A, A },
struct {
const char *design;
int value;
} designs[] = {
DESIGNS
};
which the pre-processor would expand to:
enum {
E2F = 1, E2 = 2, E3 = 3, E4 = 4, E5 = 5,
};
struct {
const char *design;
int value;
} designs[] = {
{ "E2F", E2F }, { "E2", E2 }, { "E3", E3 }, { "E4", E4 }, { "E5", E5 },
};
And here is #DavidCRankin's suggestion (if I understood it right) to just store the array to derive the value from the index:
#include <string.h>
int design_to_number(const char *str) {
const char *designs[] = { "E2F", "E2", "E3", "E4", "E5" };
for(int i = 0; i < sizeof(designs) / sizeof(*designs); i++) {
if(!strcmp(designs[i], str)) return i + 1;
}
return -1;
}
C enum values are just named integers. For string conversion, you'll need to roll our own (unlike Java, for example, where enums are more powerful). One way to go about conversion from string to enum is use the library bsearch function:
#include <stdlib.h>
#include <assert.h>
#include <string.h>
enum design {E2F = 1, E2, E3, E4, E5};
struct design_value {
const char *design;
enum design value;
} designs[] = {
{"E2", E2},
{"E2F", E2F},
{"E3", E3},
{"E4", E4},
{"E5", E5},
};
static int design_value_cmp(const void *a, const void *b) {
return strcmp(((struct design_value*) a)->design, ((struct design_value*) b)->design);
}
enum design get_design(char *designation) {
struct design_value key[1] = {{ designation }};
struct design_value *result = (struct design_value*) bsearch(
key,
designs, sizeof designs / sizeof designs[0], sizeof designs[0],
design_value_cmp);
assert(result);
return result->value;
}
// Tiny verifier. Don't use scanf("%s"...) in real code.
#include <stdio.h>
int main(void) {
char buf[100];
scanf("%s", buf);
printf("%d\n", get_design(buf));
return 0;
}
Note bsearch requires that the strings be in alpha order.

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.

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"

Resources