I'm having issues writing a function that allocates a struct in C. Ideally, I want to have the function fill the fields of the struct with parameters passed into it.
I have defined the struct in my header file like so:
typedef struct {
char name[NAME_SIZE]; //Employee name
int birthyear; //Employee birthyear
int startyear; //Employee start year
} Employee;
And this is what I have for my function currently:
void make_employee(char _name, int birth_year, int start_year) {
Employee _name = {_name,birth_year,start_year}; //allocates struct with name
} /* end make_employee function */
Any advice on how to accomplish this?
The problem with your current code is that the struct your creating is created on the stack and will be cleaned up as soon as the function returns.
struct foo
{
int a;
int b;
};
struct foo* create_foo( int a, int b )
{
struct foo* newFoo = (struct foo*)malloc( sizeof( struct foo ) );
if( newFoo )
{
newFoo->a = a;
newFoo->b = b;
}
return newFoo;
}
This will get you a heap allocated object. Of course, you'll need a function to free that memory or this is a memory leak.
void destroy_foo( struct foo* obj )
{
if( obj )
free( obj );
}
void print_foo( struct foo* obj )
{
if( obj )
{
printf("foo->a = %d\n",obj->a);
printf("foo->b = %d\n",obj->b);
}
}
(btw, this style gets you part of the way toward an "object oriented" C. Add some function pointers to the struct (to get polymorphic behavior) and you have something interesting; though I'd argue for C++ at that point.)
You have to return a pointer allocated via malloc:
Employee* new_employee(char *_name, int birth_year, int start_year) {
struct Employee* ret = (struct Employee*)malloc(sizeof(struct Employee));
ret->name = _name;
ret->birth_year = birth_year;
ret->start_year = start_year;
return ret;
}
two more things: (1) you should make the struct definition of name a char* instead of char[NAME_SIZE]. Allocating a char array makes the struct much bigger and less flexible. All you really need is a char* anyway. And (2) change the function definition to char*.
Why does the make Employee return void? You need to return the Employee from the make_employee function!
Are you having trouble with the compiler complaining about the x = {a,...} syntax? Write it the long way then: Emp e; e.field1 = a; ...
Are you having weird overwriting / bogus numbers problems? If you allocate a struct in the function it will become invalid (and prone to being overwriten) as soon as the function returns! To go around this you either have to:
Return a copy of the struct (this is OK for small structs):
Employee make_emp(int a){
Emp emp; //Allocate temporary struct
emp.filed1 = a; //Initialize fields;
return emp; // Return a copy
}
Allocate the struct in the heap instead and deal with it through references (ie.: pointers) instead:
Employee* make_emp(int a){
Emp* emp = malloc(sizeof(Emp)); //Allocate the struct on the heap
//And get a reference to it
emp->filed1 = a; //Initialize it
return emp; //Return the reference
}
Don't forget to free() the Employee after you are done with it in this case!
Employee * make_employee(char *_name, int birth_year, int start_year)
{
Employee *employee;
if (employee = (struct Employee *)memalloc(sizeof(Employee)) == NULL)
{
return NULL;
}
else
{
strcpy(&(employee->name), _name);
employee->birthyear = birth_year;
employee->startyear = start_year;
return employee;
}
}
Related
I have the struct:
struct mystruct {
int a;
};
If I create a function with the struct as an argument,
and try to directly return its address:
struct mystruct *
modifystruct1(struct mystruct s)
{
s.a = 5;
return &s;
}
Compiling with c99 -Wall -Wextra -pedantic will warn warning: function returns address of local variable [-Wreturn-local-addr],
which I know I shouldn't do.
However, if I save the address to another variable
and try to return that, the warning disappears:
struct mystruct *
modifystruct2(struct mystruct s)
{
struct mystruct *sptr = &s;
sptr->a = 5;
return sptr;
}
Is this okay to do, or is it no different from the above?
(and if so, why is there no more warning?)
If not, how can I modify a copy of a struct inside a function
and return a pointer to that struct,
safely, preferably without using malloc?
Is this okay to do, or is it no different from the above? (and if so,
why is there no more warning?)
In fact it is the same. After exiting the function
struct mystruct *
modifystruct2(struct mystruct s)
{
struct mystruct *sptr = &s;
sptr->a = 5;
return sptr;
}
the returned pointer will be invalid because it points to a local variable s with automatic storage duration that is not alive after exuting the function.
It seems the compiler is unable to determine that the returned pointer sptr points to a local object.
Pay attention to that the function parameter does not make sense because it is at once changed within the function.
If you want to change the object of the structure type passed to the function then pass it by reference through a pointer it it like
void modifystruct2(struct mystruct *s)
{
s->a = 5;
}
If you want to create an object of the structure type within the function and to return it then define the function for example the following way
struct mystruct modifystruct2( void )
{
struct mystruct s = { .a = 5 };
return s;
}
Pass reference to the struct instead of struct itself (but it will modify the original struct)
struct mystruct *
modifystruct2(struct mystruct *s)
{
s->a = 5;
return s;
}
Dynamically allocate the new struct
struct mystruct *
modifystruct2(struct mystruct s)
{
struct mystruct *sptr = malloc(sizeof(*sptr));
if(sptr)
{
*sptr = s;
sptr->a = 5;
}
return sptr;
}
or to do not pass the whole struct to the function
struct mystruct *
modifystruct2(struct mystruct *s)
{
struct mystruct *sptr = malloc(sizeof(*sptr));
if(sptr)
{
*sptr = *s;
sptr->a = 5;
}
return sptr;
}
return struct by value
struct mystruct
modifystruct2(struct mystruct s)
{
s.a = 5;
return s;
}
at function call: modifystruct2(&s); (pass the struct adress)
I would use a typedefstruct.
void modifystruct2(struct mystruct *s)
{
s->a = 5;
return ;
}
im trying to make an array of struct and initialize struct array member, but I don't know how to access struct member, i used (st->ch)[t] = 'c'; and others similar syntax but i did not succeed.
best regards.
struct ST
{
char ch;
};
bool init(ST* st, int num)
{
st = (ST*)malloc(num * sizeof(ST));
if (st == NULL) return false;
for (int t = 0; t < num; t++) (st->ch)[t] = 'c';
return true;
}
int main()
{
ST* s = NULL;
init(s, 2);
putchar(s[1].ch);
}
You declared in main a pointer
ST* s = NULL;
that in C shall be declared like
struct ST* s = NULL;
because you declared the type specifier struct ST (that in C is not the same as just ST)
struct ST
{
char ch;
};
that you are going to change within a function. To do that you have to pass the pointer to the function by reference. That is the function declaration will look at least like
bool init( struct ST **st, int num );
and the function is called like
init( &s, 2);
if ( s ) putchar( s[1].ch );
The function itself can be defined like
bool init( struct ST **st, int num )
{
*st = malloc( num * sizeof( struct ST ) );
if ( *st )
{
for ( int i = 0; i < num; i++) ( *st )[i].ch = 'c';
}
return *st != NULL;
}
If you are using a C++ compiler then substitute this statement
*st = malloc( num * sizeof( struct ST ) );
for
*st = ( struct ST * )malloc( num * sizeof( struct ST ) );
When the array of structures will not be needed you should free the memory occupied by the array like
free( s );
You can access struct member using following:
st[t].ch
As mentioned by #kaylum st being a local variable in the init() and doesn't update the variable s in the main function hence an alternative could be you either pass the add the address of the variable s to init() or could just return the allocated memory as shown in the code snippet below. Instead of using bool as return type to check you can use the ST* as return type and if it returns NULL or mem address to get mem alloc status.
Also you would have to typedef the struct typedef struct ST ST; to be able to directly use the type as ST else you would have to stick to using struct ST
typedef struct ST
{
char ch;
}ST;
ST* init(int num)
{
ST *st;
// Create num elems of ST type
st = (ST*)malloc(num * sizeof(ST));
// return NULL is st unintialised
if (st == NULL) {
return st;
}
// Assign ch member variable of the 't'th st element wit 'c'
for (int t = 0; t < num; t++) {
st[t].ch = 'c';
}
return st;
}
int main()
{
ST* s;
// creates an array of size two of type st
s = init(2);
putchar(s[1].ch);
return 0;
}
How can I initialize array dynamically in struct Course? I need to make array of student structs.
typedef struct {
char *name;
char ID[9];
} Student;
typedef struct {
Student *students = //here
} Course;
Initializing in a struct declaration isn't possible, and it wouldn't make sense in C -- you don't have an object of that struct yet.
Assuming you need a variable amount of Students in your array, there are different ways to model that. A typical approach could look like:
typedef struct {
size_t capacity;
size_t count;
Student **students;
} Course;
With the double-pointer, this is designed to hold "references" to the Student objects (instead of the Student objects themselves). I have to guess this is what you need. You could allocate and manage that for example like this:
#define CHUNKSIZE 16 // reserve space for this many Students at once
Course *Course_create(void)
{
Course *course = malloc(sizeof *course);
if (!course) return 0;
course->capacity = CHUNKSIZE;
course->count = 0;
course->students = malloc(CHUNKSIZE * sizeof *(course->students));
if (!course->students)
{
free(course);
return 0;
}
return course;
}
int Course_addStudent(Course *course, const Student *student)
{
if (course->count == course->capacity)
{
// allocate more memory if needed
size_t newcapa = course->capacity + CHUNKSIZE;
Student **newstudents = realloc(course->students, newcapa * sizeof *newstudents);
if (!newstudents) return 0; // error
course->capacity = newcapa;
course->students = newstudents;
}
course->students[course->count++] = student;
return 1; // success
}
A proper cleanup could look like this:
void Course_destroy(Course *course)
{
if (!course) return;
free(course->students);
free(course);
}
Student *students is just a pointer to Student. You cannot and should not initialize the pointer.
Method1
You need to first allocate memory for the struct and then initialize it.
// in main
Course courses;
courses.students = malloc(sizeof(Student));
if (courses.students != NULL_PTR)
{
courses.students.name = malloc(100); // 100 would be the size of the name you want to store
if (courses.students.name != NULL_PTR)
{
courses.students.name = "Default Name";
courses.students.ID = 12345;
}
}
Method2
This method removes the pointers from the structs in the first place. It changes the definition of the structure.
Since there are no pointers involved, you can safely inialize the structure on definition.
typedef struct {
char name[100];
char ID[9];
} Student;
typedef struct {
Student students;
} Course;
int main(void)
{
Course courses = {{"Default Name",12345}};
// other code here
}
I have a fight game between 4 Magicians and 1 Demon in C.
Each of them have 3 attributes :
NAME
PV(Life points)
PM(Magic points)
I had to create a structure for Magician and Demon so i wrote :
struct Magician{
char name_magician[20];
int pm;
int pv;
};
struct Demon{
char name_demon[20];
int pv;
int pm;
};
typedef struct Magician magician;
typedef struct Demon demon;
Now:
How can i create a function that create a Demon?
And a function to create a group of 4 Magicians ?
What i tried in main function basically the simple :
demon d1;
d1.name_demon="demonx123";
d1.pv=15;
d1.pm=20;
Returning a single structure from a function is easy: You do it just like any other function returning a value:
demon create_demon(void)
{
demon d;
// Initialize the demon
return d;
}
Returning an array of multiple structures is a little harder, but just like for returning a single structure is the same as returning any value, returning an array of structures is exactly the same as returning an array of any other value. This can be done either by allocating on the heap, and returning a pointer. Or by passing a pointer to the first element as an argument to the function.
To make group of Magician, you can use array of Magician structure.
Something like...
> struct Magician m[4];
Use this statement in a function.
This will create you 4 members of Magician.Now you can initialize them as you are doing for demon.
struct Magician *m = malloc (sizeof(struct Magician)*NumOfMagitians);
And return the pointer
This way you can easily create your demons.
struct demon *createDemon(char *name, int pv, int pm){
struct demon *tmp = malloc(sizeof(struct demon));
if (tmp == NULL || sizeof(tmp->name) < strlen(name))
return NULL;
for (int i = 0; i < strlen(name); i++)
tmp->name[i] = name[i];
tmp->pm = pm;
tmp->pv = pv;
return tmp;
}
And in case you wonder, this way you can remove it again.
void removeDemon(struct demon *dtr){
if (dtr != NULL){
free(dtr);
}
}
To make your structure a little less static you should however change
the way you initialize the name of your creature, like:
struct demon{
char *name;
int pv;
int pm;
};
This way you can allocate the space you need for your creatures name dynamically like:
struct demon *createDemon(char *name, int pv, int pm){
// Try to allocate demon structure. If fail return NULL
struct demon *tmp = malloc(sizeof(struct demon));
if (tmp == NULL)
return NULL;
// Try to allocate demon name. If fail, return NULL
tmp->name = malloc(strlen(name));
if (tmp->name == NULL) {
free(tmp->name);
return NULL;
}
// Set Variables and return structure
tmp->name = name;
tmp->pm = pm;
tmp->pv = pv;
return tmp;
}
And you need to renew your removal function as well to remove the allocated space created for the name like:
void removeDemon(struct demon *dtr){
if (dtr != NULL){
free(dtr->name);
free(dtr);
}
}
I am trying to understand how to pass a struct by reference in order to create a linked list. The method I am using is similar to the example code given below. However, when this code is run, the *tester declared in the main function always stays as NULL. Is the passing of a struct to the addNode() function in this way inappropriate (the compiler does not raise any warnings)?
struct test{
int num;
struct test *next;
};
void addNode (int num, struct test* tester);
int main (void){
struct test *tester = null;
addNode(1, tester);
}
void addNode(int num, struct test* tester){
struct test *example = malloc(sizeof(struct test));
example->num = num;
if (tester == NULL){
tester = example;
} else{
tester->next = example;
}
}
In addNode function the pointer tester no longer points to the location pointed by the tester in main. function and change your function to
void addNode(int num, struct test** tester){
struct test *example = malloc(sizeof(struct test));
if (NULL == example )
exit(0); // Not enough memory
example->num = num;
if (NULL == *tester)
*tester = example;
else
(*tester)->next = example;
}
Call this function from main as addNode(1, &tester);. Now *tester is an alias for tester in main.
First off, you're assigning NULL to your input. This:
if (tester = NULL)
should be
if (tester == NULL)
Secondly, in that same branch, you assign a new value to tester. However, everything in C is passed by value (copy), so your function receives a copy of a pointer. Therefore, you are only mutating the function's local copy. You need another level of indirection:
#include <assert.h>
struct test{
int num;
struct test *next;
};
void addNode (int num, struct test* tester);
int main (void){
struct test *tester = NULL;
addNode(1, &tester);
}
void addNode(int num, struct test** tester){
/ * wrong, check next item */
assert(tester != NULL);
struct test example = malloc(sizeof(struct test));
example->num = num;
if (*tester == NULL){
*tester = example;
} else{
(*tester)->next = example;
}
}
Last, malloc returns a void*, which can implicitly be converted to any other type of pointer. It does not however return an "instance". So this is wrong:
struct test example = malloc(sizeof(struct test));
and should be:
struct test *example = malloc(sizeof *example);
You are saving the pointer returned by malloc as a struct:
struct test example = malloc(sizeof(struct test));
Perhaps you wanted to store it as a pointer to struct, so that example and tester have matching types:
struct test* example = malloc(sizeof(struct test));
Then, this will make sense:
tester = example;