Reputation: 25
So, I know in C you can pass function arguments by reference or by value, but technically everything is by value (since the entities passed by reference are just addresses).
My questions is, if given a MEMORY ADDRESS that is NOT a pointer, is it possible to do malloc/calloc operations on this address?
This is a sample of what I am dealing with:
typedef struct bucket {
int instances;
char *key;
struct bucket *next;
} Bucket;
typedef struct bag {
int key_count;
int current_capacity;
Bucket **buckets;
} Bag;
void init_bag (Bag *bag){...}
In this init_bag function, I need to take the address of a Bag which is declared elsewhere and initialize it. But I don't know how I would do this if I am only given the memory address of bag, as in:
Bag bag;
init_bag(&bag);
I have previously done something like this where I took a pointer to a pointer to a bag in the init_bag function, and I used malloc on the once-dereferenced entity of the function parameter (which at this point is simple a pointer to the bag), and operations that modified this were standard pointer operations that could set a value for the actual address of the bag. This time though, I need to have the function prototype in the form I wrote above, and it needs to be able to take in the address of a bag (not simply a pointer to a bag) and do appropriate memory allocation operations on that address. Any advice on what I can do in this current situation?
Upvotes: 1
Views: 752
Reputation: 29116
In your example, the memory for bag
has already been allocated. When you define it as a local variable like so:
Bag bag;
you have created a structure in local storage whose contents are uninitialised. Your function init_bag
should therefore not allocate memory, it should just initialise the fields of the struct, for example:
void init_bag(Bag *bag) {
bag->key_count = 0;
bag->current_capacity = MAX_BAG;
bag->buckets = calloc(MAX_BAG, sizeof(*bag->buckets));
}
After you return from this function, you have a valid bag
: You have filled in valid fields into an already existing struct, which you have accessed via a pointer.
If you had passed just the struct, you would have initialised a struct local to init_bag
. This struct would have been lost after returning from init_bag
and your bag
in the calling function would have been as uninitialised as before. The reason to pass the address of your bag
is that you can access your bag
object that was created in the calling function from within init_bag
via a pointer. Taking the address of an object creates a pointer to that object.
On the other hand, the code you describe in your last paragraph would lead to something like this:
void new_bag(Bag **bag) {
*bag = malloc(sizeof(**bag);
(*bag)->key_count = 0;
(*bag)->current_capacity = MAX_BAG;
(*bag)->buckets = calloc(MAX_BAG, sizeof(*(*bag)->buckets));
}
And you would call it like this:
Bag *bag;
new_bag(&bag);
This code is very similar to the first snippet, but it does something else. It will initialise the Bag
struct just as above, but it will create the bag
object on the heap first. The bag will still be valid if you leave the current function, whereas the lifetime of the bag created with
Bag bag;
init_bag(&bag);
is limited to the current scope. Which variant you prefer (or whether you should implement both) depends on how you want to use the bag.
(The whole discussion here is really about the allocation of memory for the bag. It does not refer to the allocation of memory for the buckets. Because your struct requests resources, you should also have a clean-up function that releases these resources again.
If you have chosen to allocate the bag on the heap with malloc, you must, of course, also free that memory when you clean up the struct.)
Upvotes: 2