Initializing struct within a function then storing its address - c

Hi I have a struct like this
struct small_struct {
int a;
int b;
}
struct big_struct {
struct *small_struct child;
}
I want to pass the pointer of big_struct as a parameter into a function in which child is initialized.
static int my_function(struct big_struct* s) {
if (certain_condition)
s->child = &(struct small_struct) {
.a = 1;
.b = 2;
}
}
However, when I do this and my_function is finished, the fields in s->child are often changed outside of the my_function. Would there be a way to keep a and b values as it was initialized inside my_function?
Thank you!

The problem is here:
s->child = &(struct small_struct) {
.a = 1;
.b = 2;
}
This creates the struct in the stack memory of the function, then assigns the s->child pointer to that memory. As soon as the function returns, that memory is no longer allocated to your struct. What you need to do is allocate heap memory for the structure, using malloc, which will stay allocated until it is free'd with a call to free:
static int my_function(struct big_struct* s) {
if (certain_condition)
{
//Allocate *heap* memory for the pointer
//This must be freed later!
//e.g free(s.child);
s->child = malloc(sizeof(struct small_struct));
s->child->a = 1;
s->child->b = 2;
}
Alternatively, depending on what you are trying to do, don't make child a pointer, that way the memory is already allocated in the instance of big_struct e.g.:
struct big_struct
{
struct small_struct child; //Note: not a pointer
};
static int my_function(struct big_struct* s) {
if (certain_condition)
{
//Memory for child member is already allocated
s->child.a = 1;
s->child.b = 2;
}
}

Related

How to not "return address of local variable" when modifying a copy of a struct?

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 ;
}

Allocating memory for Heap with mallocs

I'm having problems declaring a new heap, empty, with max size "capacity".
Heap struct:
typedef struct {
/* number of elements on vector */
int size;
/* vector max size */
int capacity;
/*vector of pointers for elements*/
element_t** elements;
} heap;
Element_t struct:
typedef struct element_
{
char nameItem[100];
char expirationDate[11];
int qty;
int sellRate;
float priorityVal;
} element_t;
The function that I need to create the heap is declared like that, where the argument capacity is the heap capacity.
heap* new_heap(int capacity){
Function that insert elements in Heap:
int heap_insert(heap *h, element_t* elem)
{
element_t * aux;
int i;
//gilc
if(!h) return 0;
/* if heap is full, dont insert element */
if (h->size >= h->capacity)
return 0;
if (!elem)
return 0;
/* insert element in the end of the heap */
h->size++;
i = h->size;
h->elements[i] = elem;
/* while element has more prioritary than his father, trade them */
while (i != ROOT && bigger_than(h->elements[i], h->elements[FATHER(i)]))
{
aux = h->elements[FATHER(i)];
h->elements[FATHER(i)] = h->elements[i];
h->elements[i] = aux;
i = FATHER(i);
}
return 1;
//Default
return 0;
}
FATHER and ROOT is defined like that (I don't understand what that means, was pre-defined for the project too)
#define FATHER(x) (x/2)
#define ROOT (1)
and bigger_than like this:
int bigger_than(element_t* e1, element_t* e2)
{
if (e1 == NULL || e2 == NULL)
{
return 0;
}
return e1->priorityVal > e2->priorityVal;
}
What malloc calls do I need to use? The function new_heap must allocate all memory necessary for the number of elements specified as argument capacity.
heap *new_heap(int capacity) {
heap *h = malloc(sizeof(heap));
h->size = 0;
h->capacity = capacity;
h->elements = malloc(capacity * sizeof(element_t *));
return h;
}
The first malloc will make enough space for your heap structure. The second is for "vector" (as you called it) of pointers to elements, since these need to be stored in a separate spot in memory (based on your declaration of heap). Together, this allocates all the memory you need for the heap. I'm assuming you'll also have a new_element function that will handle allocating the memory for an individual element for you whenever you want to add something to the heap.

C - Check if index of struct array is uninitialized

I'm making a HashMap in C but am having trouble detecting when a Node has been initialized or not.
Excerpts from my code below:
static struct Node
{
void *key, *value;
struct Node *next;
};
struct Node **table;
int capacity = 4;
table = malloc(capacity * sizeof(struct Node));
// At this point I should have a pointer to an empty Node array of size 4.
if (table[0] != NULL)
{
// This passes
}
I don't see what I can do here. I've read tons of other posts of this nature and none of their solutions make any sense to me.
malloc does not initialize the memory allocated. You can use calloc to zero-initialize the memory.
// Not sizeof(struct Node)
// table = calloc(capacity, sizeof(struct Node));
table = calloc(capacity, sizeof(*table));
After that, it will make sense to use:
if (table[0] != NULL)
{
...
}
I suggest you consider something like a HashMapCollection type that you create with a set of functions to handle the various memory operations you need.
So you might have code something like the following. I have not tested this nor even compiled it however it is a starting place.
The FreeHashMapCollection() function below would process a HashMapCollection to free up what it contains before freeing up the management data structure. This may not be what you want to do so that is something for you to consider.
The idea of the following is to have a single pointer for the HashMapCollection struct and the array or list of HashMapNode structs immediately follows the management data so a single free() would free up everything at once.
typedef struct _TAGHashMapNode {
void *key, *value;
struct _TAGHashMapNode *next;
} HashMapNode;
typedef struct {
int iCapacity; // max number of items
int iSize; // current number of items
HashMapNode *table; // pointer to the HashMapNode table
} HashMapCollection;
Then have a function to allocate a HashMapCollection of a particular capacity initialized properly.
HashMapCollection *AllocateHashMapCollection (int iCapacity)
{
HashMapCollection *p = malloc (sizeof(HashMapCollection) + iCapacity * sizeof(HashMapNode));
if (p) {
p->table = (HashMapNode *)(p + 1);
p->iCapacity = iCapacity;
p->iSize = 0;
memset (p->table, 0, sizeof(HashMapNode) * iCapacity);
}
return p;
}
HashMapCollection *ReallocHashMapCollection (HashMapCollection *p, int iNewCapacity)
{
HashMapCollection *pNew = realloc (p, sizeof(HashMapCollection) + sizeof(HashMapNode) * iNewCapacity);
if (pNew) {
pNew->table = (HashMapNode *)(pNew + 1);
if (p == NULL) {
// if p is not NULL then pNew will have a copy of that.
// if p is NULL then this is basically a malloc() so initialize pNew data.
pNew->iCapacity = pNew->iSize = 0;
}
if (iNewCapacity > pNew->iCapacity) {
// added more memory so need to zero out that memory.
memset (pNew->table + iCapacity, 0, sizeof(HashMapNode) * (iNewCapacity - pNew->iCapacity));
}
pNew->iCapacity = iNewCapacity; // set our new current capacity
p = pNew; // lets return our new memory allocated.
}
return p; // return either old pointer if realloc() failed or new pointer
}
void FreeHashMapCollection (HashMapCollection *p)
{
// go through the list of HashMapNode items and free up each pair then
// free up the HashMapCollection itself.
for (iIndex = 0; iIndex < p->iCapacity; iIndex++) {
if (p->table[iIndex].key) free (p->table[iIndex].key);
if (p->table[iIndex].value) free (p->table[iIndex].value);
// WARNING ***
// if these next pointers are actually pointers inside the array of HashMapNode items
// then you would not do this free as it is unnecessary.
// this free is only necessary if next points to some memory area
// other than the HashMapNode table of HashMapCollection.
if (p->table[iIndex].next) free (p->table[iIndex].next);
// even though we are going to free this, init to NULL
p->table[iIndex].key = NULL;
p->table[iIndex].value = NULL;
p->table[iIndex].next = NULL;
}
free (p); // free up the memory of the HashMapCollection
}

my callback struct won't work

I have this code:
/* ... headers ... */
struct my_callback_struct {
int data;
int (*callback_func)(struct my_callback_struct *p, int data);
};
int reg(struct my_callback_struct *p, int data)
{
return 1;
}
void init(struct my_callback_struct *p)
{
p->callback_func = ®
}
int main ()
{
struct my_callback_struct *p;
init(p);
printf("%d", p->callback_func(p,12));
return 0;
}
so, basically I try to map p->callback_func with the function "reg". But when i compile it it says no errors but a segmention fault or something. I put a return of 1 just to check if it works...
What am i doing wrong?
You simply haven't allocated memory for your struct instance. Try something like
struct my_callback_struct s;
init(&s);
printf("%d", s.callback_func(p,12));
Or call malloc for your p before calling init.
You need to allocate your callback struct. The code as written is only a pointer to it. Two opens are:
struct my_callback_struct p; // Just "p", not "*p" which is a pointer
This allocates the struct on the stack. Its memory will no longer be allocated when the enclosing function returns. If you want it to have a longer lifetime you would most commonly use malloc() to dynamically allocate it:
struct my_callback_struct *p = (my_callback_struct*)malloc( sizeof( my_callback_struct ) );
if ( p == NULL ){
return -1; // Indicate failure to allocate
}

Heap and Stack issues with Structs - C programming

I have the following code:
#include <stdlib.h>
#include <stdio.h>
struct B
{
int _arr[5];
};
struct A
{
struct B *_pb_arr;
};
int main()
{
int i,j;
struct B b;
struct B *pb = (struct B*)malloc(sizeof (struct B));
*pb = b;
struct A a;
a._pb_arr = (struct B*)malloc(sizeof (struct B)*2);
a._pb_arr[0] = b; //first question
a._pb_arr[1] = *pb; //second question
for (i=0;i<2;++i)
{
for (j=0;j<5;++j)
{
a._pb_arr[i]._arr[j] = i;
}
}
struct A a2 = a;
for (i=0;i<2;++i)
{
for (j=0;j<5;++j)
{
printf ("%d, ", a2._pb_arr[i]._arr[j]);
}
}
return 0;
}
My question is: why on: a._pb_arr[0] = b;
The assignment is on stack.
and on the next line: a._pb_arr[1] = *pb
The assignment is on the heap?
It seems like a.pb_arr was allocated on the heap and each assignment is on the heap also.
Assignments aren't on the stack or heap, objects are.
Object a is on the stack because it's created like this:
struct A a;
To create objects on the heap, you have to allocate memory for them using malloc.
So although a is on the stack, a._pb_arr will be on the heap. You have to explicitly free the memory when you're done. Following the same reasoning, b is on the stack and pb is on the heap.
When you assign the objects in the array a._pb_arr, the values will be copied, but they still remain on the heap, since you allocated memory to a._pb_arr.
To prevent memory leaks, you have to use free for both a._pb_arr and pb.

Resources