Custom implementation of dynamic memory allocators in C - c

So for my C assignment, I need to implement a dynamic memory allocator with a similar interface to the standard library like malloc, free, realloc. I'm implementing the allocator as a library of functions that can be called by other programs. Virtual heap will be managed by a simple buddy allocation algorithm.
My functions given are:
void * virtual_sbrk(int32_t increment);
pretty much the same as the real-world sbrk and brk syscalls. I don't need to implement this.
void init_allocator(void * heapstart, uint8_t initial_size, uint8_t min_size);
This function will be called once at the beginning and initialise the virtual heap.
void * virtual_malloc(void * heapstart, uint32_t size);
mallocs memory
int virtual_free(void * heapstart, void * ptr);
frees memory
void * virtual_realloc(void * heapstart, void * ptr, uint32_t size);
reallocates memory
void virtual_info(void * heapstart);
prints the current state of the buddy allocator to standard output.
This is my current problem:
How do you initialise the heap and implement malloc without anything in the first place? Like I can't use malloc or any of the pre existing allocator functions. So far I've tried to use a linked list with nodes containing the memory as a value. Eg if initial size is 3 and min size is 1, I'd have 5 nodes with the root containing 8 bytes, two more containing 4 bytes each , and lastly 2 more contining 2 bytes each. But I'm still confused on how to use sbrk or how the heap is structured in the first place. I've browsed online resources but still confused on how to construct the heap memory.
Following is my code so far:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
struct node{
size_t memory;
struct node *nextInLine;
};
void printNode(const struct node *nd, const char *comment){
if(nd == NULL){
printf("%s is null\n", comment);
}
else{
printf("%s: memory:%d address:%p nextInLine:%p\n",
comment,
nd->memory,
nd,
nd->nextInLine);
}
}
void printList(const struct node *list){
printf("Printing List:\n");
const struct node *t;
t = list;
if(t == NULL){
printf("current node is empty\n");
}
else{
while(t){
printNode(t, "node");
t = t->nextInLine;
}
}
}
void * virtual_sbrk(int32_t increment) {
void *p = malloc(increment);
return p;
}
uint8_t return_init_size(uint8_t size){
return size;
}
struct node *getNewNode(const uint8_t memory_size){
struct node *newNode = NULL;
double two = 2;
size_t m_size = memory_size;
double result = pow(two, m_size);
newNode = virtual_sbrk(result);
if(newNode != NULL){
newNode->memory = result;
newNode->nextInLine = NULL;
}
else{
printf("Allocation error: newNode is still NULL\n");
}
return newNode;
}
void init_allocator(void * heapstart, uint8_t initial_size, uint8_t min_size) {
//error catchers
if(initial_size == 0){
printf("Initial size is 0\n");
}
if(initial_size < min_size){
printf("initial_size is smaller than min_size\n");
}
//initialising the virtual heap using a linked array with nodes the memory size of 2^some_size
uint8_t i = initial_size;
struct node *first = heapstart;
heapstart = first;
struct node *tail = NULL;
while(i >= min_size){
if(first == NULL){
first = getNewNode(i);
if(first != NULL){
tail = first;
}
}
else{
tail->nextInLine = getNewNode(i);
if(tail->nextInLine != NULL){
tail = tail->nextInLine;
}
tail->nextInLine = getNewNode(i);
if(tail->nextInLine != NULL){
tail = tail->nextInLine;
}
}
i -= 1;
}
printList(first);
}
void * virtual_malloc(void * heapstart, uint32_t size) {
if(size == 0){
return NULL;
}
return NULL;
}
int virtual_free(void * heapstart, void * ptr) {
return 1;
}
void * virtual_realloc(void * heapstart, void * ptr, uint32_t size) {
return NULL;
}
void virtual_info(void * heapstart) {
}
It would be great if someone could help explain how I would go about doing this, as in the structure I need to follow, if that makes sense.

You can use both sbrk and mmap as glibc malloc does.
glibc malloc works with threads, with something called arenas.
When malloc is initialized it calls sbrk to extend the mapped memory.
When big allocations happen, or new threads are created malloc ends up calling mmap.
mmap allocates a new mapping in the address space of the process.
sbrk extends the current mapping to make it bigger.
Simple example of sbrk:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#define HEAP_SZ 0x8000
int main(void) {
void *p = sbrk(0);
printf("current break addr = %p\n", p);
sbrk(HEAP_SZ);
void *n = sbrk(0);
printf("new break addr = %p\n", n);
return 0;
}
The first call (with argument 0) returns the current program break.
When specifying a size greater than 0, program break is extended, so on the next call with argument 0, the new program break will be returned.
You can do then something like this:
unsigned long heap_mem_sz = 0;
void *heap_start_addr = NULL;
void init_heap(void) {
void *p = sbrk(0);
#if DEBUG
printf("current break addr = %p\n", p);
#endif
sbrk(HEAP_SZ);
heap_mem_sz = (unsigned long)HEAP_SZ;
void *n = sbrk(0);
#if DEBUG
printf("new break addr = %p\n", n);
#endif
heap_start_addr = (void *)n;
return;
}
Having that information on globals allows you to continue the development of the allocator implementation.
You can call init_heap() the first time an allocation is requested.
Now you can return that allocation and craft a "top chunk".
It will be a chunk with the same structure than the others but containing all the memory from which allocations take memory, and it gets shrinked on allocations.
Also, you will need to do something once the heap memory is full, so consider calling syscalls like mmap or sbrk again.
Linked lists on malloc are used for bins. They are used for searching freed chunks that can satisfy new allocations so you reuse chunks that are not used anymore.
For such linked list, you can create a global:
struct heap_chunk *freed_chain = NULL
When memory is requested, you first check if freed_chain is NULL, if not, traverse the linked list until a block compatible with the user request is found, or the next pointer is NULL.
If any of those chunks is valid, you will need to unlink that chunk from the linked list, and make the previous chunk point to the next one, so no more memory requests access to it as now it is allocated and not freed.
On freeing memory, you would need to link a new chunk to that linked list.
Obviously on malloc, for optimization purposes this is more complex, and some different bins with different size requirements and different properties exist to speed up allocations.

Related

Design a Stack structure with allocating function

I'm trying to implement stack in C.
What I'm going for:
Design a Stack structure with push and pop functions for the implementation.
Create a Memory structure owns number of block, block size, and stack attributes.
Stack attribute represents memory blocks. Number of block attribute represents the length of the stack. Block size represents the maximum value that each element in the stack can get.
Write an allocate function that takes a size parameter. If the given size is bigger than block size of the Memory, the allocation will be distributed to the different blocks in the stack attribute.
For example, calling allocate(27) updates the stack as:
allocate(27) = [10, 10, 7, 0, 0]
for a Memory with number of block = 5, block size = 10. The remaining of the elements which don't have maximum value can be sealed until the element is flushed. Therefore, the next allocation can start from next element position after 7 given above.
Write a deallocate function that flushes the last used block.
My Work:
First, created the structs:
stack attribute represents memory blocks.
number of block attribute represents the length of the stack.
Block size represents the maximum value that each element in the stack can get.
#include <stdio.h>
#include <stdlib.h> // Provides function For memory locating. allocate & deallocate.
struct Stack {
int top;
};
struct Memory{
int stack;
int number_of_block; // 5
int block_size; // 10
};
Then I have tried to create allocate & push function but they are not working.
int main(){
allocate(30);
return 0;
}
int allocate(int size){
struct Stack* stack = (struct Stack*)malloc(sizeof(struct Stack));
struct Memory* memory = (struct Memory*)malloc(sizeof(struct Memory));
memory->block_size = 10;
stack->top = -1;
memory->stack = (int*)malloc(memory->block_size * sizeof(int));
struct Memory memory = {1, 5, 10};
for(int i = 0; i < 5; i++){
if(size > 10){
size = size - 10; //27 - 10 = 17 -> 17 - 10 = 7
push(stack, 10);
}
}
if(size % 10 != 0){
int size_mod = size % 10; //27 % 10 = 7
push(stack, size_mod);
}
}
void push(struct Stack* stack, struct Memory* memory, int item){
if(stack->top == memory->block_size - 1){
return;
}
memory->stack[++stack->top] = item;
printf("%d ", item);
}
The Memory structure contains a pointer to the first block, as well as the number of blocks and the maximum size of each block. Each block then contains the data and a pointer to the next block, meaning all the blocks are stored in a linked list.
The function allocate returns a pointer to the Memory structure created.
#include <stdio.h>
#include <stdlib.h>
struct Block {
int data;
struct Block *next;
};
struct Memory {
int block_count;
int block_size;
struct Block *head;
};
/* Push a new block onto the stack */
void push(struct Block **head, int data)
{
struct Block *new = malloc(sizeof *new);
if (!new) {
printf("Error: memory allocation failed");
exit(EXIT_FAILURE);
}
new->data = data;
*head = new;
/* `printf` is not needed */
printf("%d\n", data);
}
/* Returns a pointer to the `Memory` structure */
struct Memory *allocate(int size)
{
struct Memory *memory = malloc(sizeof *memory);
if (!memory) {
printf("Error: memory allocation failed");
exit(EXIT_FAILURE);
}
memory->block_count = 5;
memory->block_size = 10;
struct Block *head = NULL;
for (int i = 0; i < memory->block_count; ++i) {
int data = 0;
if (size > 10)
data = 10;
else if (size > 0)
data = size;
size -= data;
push(&head, data);
}
memory->head = head;
return memory;
}
int main(void)
{
struct Memory *memory = allocate(27);
return EXIT_SUCCESS;
}
And as you can see, you don't need to cast malloc, because it returns void *, which is automatically and safely promoted to any other pointer.

Stack and push() function

I'm doing an exercise, and want support about it. The problem is this: I have two structures (1 for the nodes of the stack, 1 for the stack). In the node structure, there is a void* data field.
I've tried to push a value on the stack but, because of void* data instead of simple data, I failed.
This is the code about the structures and the push() function.
struct upo_stack_node_s
{
void *data;
struct upo_stack_node_s *next;
};
typedef struct upo_stack_node_s upo_stack_node_t;
struct upo_stack_s
{
upo_stack_node_t *top;
size_t size;
};
/*Function for create the stack*/
upo_stack_t upo_stack_create()
{
upo_stack_t stack = malloc(sizeof(struct upo_stack_s));
if (stack == NULL)
{
fprintf(stderr, "Unable to create a stack!\n");
abort();
}
stack->top = NULL;
stack->size = 0;
return stack;
}
/*Push() function:*/
void upo_stack_push(upo_stack_t stack, void *data)
{
/* TO STUDENTS:
* Remove the following two lines and put here your implementation
*/
upo_stack_node_t *node = malloc(sizeof(struct upo_stack_node_s));
node->data = data; /*<-- Here's the problem */
node->next = stack->top;
stack->top = node;
++stack->size;
}
/*Top() function*/
void* upo_stack_top(const upo_stack_t stack)
{
/* TO STUDENTS:
* Remove the following two lines and put here your implementation
*/
return (void *)(stack->top); //<---
}
/*Function for testing (there are other functions in the code)*/
void test_top()
{
int value1 = 1;
int value2 = 2;
upo_stack_t stack;
stack = upo_stack_create();
upo_stack_push(stack, &value1); //<----
upo_stack_push(stack, &value2); //<----
assert( upo_stack_top(stack) != NULL );
assert( *((int*) upo_stack_top(stack)) == value2 ); <-- Here's the error
upo_stack_pop(stack, 0);
assert( upo_stack_top(stack) != NULL );
assert( *((int*) upo_stack_top(stack)) == value1 );
upo_stack_pop(stack, 0);
assert( upo_stack_top(stack) == NULL );
upo_stack_destroy(stack, 0);
}
You always have to pass a void pointer. That means if you want to pass a simple value, like 1, what you need to do is, is to allocate an integer value, and pass the pointer to it (as a void pointer).
Thus something like:
int x = 4;
upo_stack_push(upo_stack, &x);
Of course you have to make sure the int variable x does not go out of scope, otherwise the pointer will point to freed memory, which results in nasty memory problems.
Update
It is assumed above, that the void pointer you pass is stored already in memory for the scope of the stack. In case, you want the stack itself to copy the data, you have also to malloc space for that, thus not only mallocing the node, but also mallocing and copy the data type passed. Also to know the size of the data (since it is a void pointer, which is unaware of its type), you have to add an int size parameter to the push function.
For an integer value to be stored, pass it as a pointer, with size: sizeof(int). Than for copying the data structure, use memcpy. This only works for simple types and structures without pointers. If you have to copy structures with pointers (thus you need so called deep-copying), than it is more tricky.

Stack pop/push segmentation fault wrong same numbers

I'm writing a stack that's a linked list of data (void type). The data I am testing, is
struct my_data {
int val;
char name[60];
};
struct my_stack_node {
void *data;
struct my_stack_node *next;
};
struct my_stack {
int size;
struct my_stack_node *first;
};
the data used to be pushed, is initialized like this:
s1 = my_stack_init(sizeof(struct my_data));
if (!s1) {
puts("Error in my_stack_init()");
exit(1);
}
printf("\ns1 initialized, size of data: %lu\n", sizeof(struct my_data));
for (int i = 0; i < NODES; i++) {
data = malloc(sizeof(struct my_data)); // We must allocate static memory
data->val = i;
sprintf(data->name, "Value %d", i);
if (my_stack_push(s1, data)) {
puts("Error in my_stack_push()");
exit(1);
}
} //s1 is the stack we are using here
And pushing them by my_stack_push(s2, data); stack and the data as the arguments.
My push function is this one:
int my_stack_push(struct my_stack *stack, void *data){
if(stack == NULL && sizeof(data)> 0){
printf("Null Stack or data size error.\n");
//la pila debe existir
return -1;
}
else {
struct my_stack_node *nodeToPush = malloc(sizeof(struct my_stack_node));
nodeToPush -> data = data;
if(stack -> first == NULL) {
nodeToPush -> next = NULL;
stack -> first = nodeToPush;
}
else {
nodeToPush -> next = stack -> first;
stack -> first = nodeToPush;
}
}
return 0;
}
And my pop function is this one
void *my_stack_pop(struct my_stack *stack){
struct my_stack_node *node = stack->first;
if(stack->first == NULL){
return 0;
}
stack->first = node->next;
void *ret = node->data;
free(node);
return ret;
}
But in my main, when I pop them and try to compare them, I get a segmentation fault:
while ((data1 = my_stack_pop(s1))) {
data2 = my_stack_pop(fs1);
printf("Node of s1: (%d, %s)\t", data1->val, data1->name);
printf("Node of fs1: (%d, %s)\n", data2->val, data2->name);
if (!data2 || data1->val != data2->val || my_strcmp(data1->name, data2->name)) {
printf("Data in s1 and fs1 are not the same.\n (data1->val: %d <> data2->val: %d) o (data1->name: %s <> data2->name: "
"%s)\n",
data1->val, data2->val, data1->name, data2->name);
exit(1);
}
size1 = sizeof(*data1);
size2 = sizeof(*data2);
free(data1);
free(data2);
}
printf("size of data from s1: %d\t", size1);
printf("size of data from fs1: %d\n", size2);
(the 2 stacks are a copy of each other so what I inputed, should be the same I read). When I return the whole node in the pop function (not the data, but the whole my_stack_node), everything is right.. but wrong:
Comparing the data...
Node of s1: (0, Value 0) //good one
Node of fs1: (0, Value 0)
8
8
Node of s1: (-1203217792, NV) //here it begins to go all wrong
Node of fs1: (-1203217792, NV)
8
8
Node of s1: (-1203217792, NV)
Node of fs1: (-1203217792, NV)
8
8
Node of s1: (-1203217792, NV)
Node of fs1: (-1203217792, NV)
8
8
Node of s1: (0, )
Node of fs1: (0, )
double free or corruption (fasttop)
Aborted (core dumped)
Size is the same as the data inputed, but the value and name are bad (even in the non copied stack), which is supposed to be:
New node in s1: (0, Value 0)
New node in s1: (1, Value 1)
New node in s1: (2, Value 2)
New node in s1: (3, Value 3)
New node in s1: (4, Value 4)
New node in s1: (5, Value 5)
New node in s1: (6, Value 6)
New node in s1: (7, Value 7)
New node in s1: (8, Value 8)
New node in s1: (9, Value 9)
But when I return (on my stack pop) the data itself like in the code, I get core dumped in the print of the test.(data that has 8 bytes of length, like the one input).
When I return the node (size = 64) it prints correctly wrong data, but when I return the data(size = 8 (like the one pushed)), it core faults.
If I push the same data and read the same data (as it shown when I return node because even when weird output, are the same), why do I get a core segmentation fault when I return the data that's supposed to print like the example above?
It looks that it only happens when I read the data2, and not data1. This is the code I use to write and read the file :
Write:
int my_stack_write(struct my_stack *stack, char *filename){
int count = 0;
struct my_stack_node *aux =malloc(sizeof(struct my_stack_node));
FILE *file = fopen(filename, "wb");
if(stack->first != NULL){
aux = stack->first;
count++;
while(aux->next != NULL){
printf("New node in s1: (%p)\n", aux->data);
fwrite(aux ,sizeof(struct my_stack_node), 1, file);
aux = aux->next;
count++;
}
printf("New node in s1: (%p)\n", aux->data);
fwrite(aux ,sizeof(struct my_stack_node), 1, file);
}
fclose(file);
return count;
}
Read:
struct my_stack *my_stack_read(char *filename){
struct my_stack *stackRead = my_stack_init(sizeof(struct my_stack_node));
struct my_stack_node *stackNode = malloc(sizeof(struct my_stack_node));
FILE *file = fopen(filename, "rb");
if(!file){
puts("Impossible obrir el fitxer");
return NULL;
}else{
while(fread(stackNode, sizeof(struct my_stack_node), 1, file)){
printf("New node in fs1: (%p)\n", stackNode->data);
stackNode = (struct my_stack_node*) stackNode;
my_stack_push(stackRead, stackNode->data);
}
fclose(file);
struct my_stack *InvertedStack = my_stack_init(sizeof(struct my_stack_node));
struct my_stack_node *aux = malloc(sizeof(struct my_stack_node));
while(my_stack_len(stackRead) !=0){
printf("Inverting the stack\n");
aux = my_stack_pop(stackRead);
my_stack_push(InvertedStack, aux);
}
return InvertedStack;
}
}
Thank you to anyone that helps.
MCVE of the program, so people can check the whole code and help better:
test2.c:
https://codeshare.io/244eN4
my_lib.c:
https://codeshare.io/G7L8Ab
my_lib.h:
https://codeshare.io/5DzZOm
With this, you should have a broader view and an executable once compiled on what's happening to me.
you have problems in my_stack_pop
void *ret = malloc(sizeof(struct my_stack_node));
ret = node->data;
The malloc is useless (and create a memory leak), and you miss to free node too
You can replace these 2 lines by :
void * ret = node->data;
free(node);
Other remarks
in my_stack_push check the error before to do the allocation, or free nodeToPush in case of the error else you have a memory leak
sizeof(x) where x is a void* will always values 4 if you are on a 32b CPU, and 8 if it is a 64b CPU. sizeof is not strlen for instance
Finally concerning the 2 stacks are a copy of each other so what I inputed, should be the same it is difficult to help you because you do not say how you cloned the stack
(remarks after your edit)
In my_stack_write
do not initialize aux with an allocation, you again have a memory leak doing that.
to dump the memory of my_stack_node do not work, your goal is to save the data (contains of my_data), not the cell pointing to the data
In my_stack_read
it is useless to use a dynamic allocation for my_stack_node, it can be placed into the stack ( struct my_stack_node stackNode; ), else do not forget to free it because you again introduce a memory leak.
same error when you read a my_stack_node, you have to read the saved data (a my_data)
stackNode = (struct my_stack_node*) stackNode; that does nothing because it sets stackNode with itself
my_stack_push(stackRead, stackNode->data); does not have the expected result because stackNode->data has a wrong value read in the file.
Both read and write are wrong, this is why the two stack do not have the same contents
Designing good, reliable and consistent API and libraries is a very hard job, especially in programming languages in which creating interfaces is somewhat harder then in object oriented languages.
I mean to be really nice, but the code you posted has memory leaks, unhandled errors, undefined behavior and bad design. The sizeof operator is misused. I can only guess you fail to understand how the memory allocation really works and the concept of a pointer and general void* pointer.
Well, let's go.
So the reasons the code will seg fault is:
As suspected, the data to which the stack points is invalid. Indeed it is mallocated, but is subsequently freed some lines later:
for (int i = 0; i < NODES; i++) {
struct ... * data = malloc(sizeof(struct my_data));
my_stack_push(s1, data); // pushes the data pointer to the stack
void *data1 = my_stack_pop(s1); // pops the data pointer to the stack
...
assert(data1 == data); // data and data1 are the same
free(data1); // and data get's freed
// the memory behind both data and data1 is freed in this point
// thus the pointer s1.first->node->data is invalid
// as the code runs in loop, effectively all the data in this stack are invalid
}
You double free pointer in the main() in the while loop. Both s1 and fs1 are obtained from calling my_stack_read on the same file - thus logically they should contain the same values. As they store the same values, stored pointers to data are the same, thus freeing the pointer will also free and invalidate the second pointer in the second list. Double free is undefined behavior and I guess should result in something similar to segmentation fault on normal systems.
while ((data1 = my_stack_pop(s1))) {
data2 = my_stack_pop(fs1);
...
assert(data1 == data2); // same pointers
free(data1);
free(data2); // double free corruption - seg fault
}
After fixing the errors the code will run and print "All tests passed", live version available here. Anyway below are some notes:
There is no need to allocate an array of stacks in my_stack_init:
struct my_stack *my_stack_init(int size){
struct my_stack *stack = malloc(sizeof(struct my_stack_node) * size);
...
stack now points to size count of sizeof(struct my_stack_node) bytes of memory. You need only a single my_stack_node structure. Also, sizeof returns size_t. A better version would be:
struct my_stack *my_stack_init(size_t size){
struct my_stack *stack = malloc(sizeof(struct my_stack_node));
...
my_stack_read leaks memory:
struct my_stack_node *aux = malloc(sizeof(struct my_stack_node));
...
aux = my_stack_pop(stackRead);
Indentation in the code you posted is a bit off. Try to keep one indentation style, I could advertise the good old' Linux kernel coding style but you can use anything but be consistent. Also your code could use some restructuring - limiting the scope of variables or like removing else after return NULL may increase readability.
sizeof returns size_t. The proper way to print size_t is to use "%zu" printf modifier. You can print pointers by casting to void* and using "%p" printf modifier.
Generally, this is really good work, but you need to understand that pointers point to data and are data themselves (as they have value). Currently your implementation stores only pointers to the data, thus client code is responsible for freeing the pointers. It's easy to trap yourself in confusion in such implementation. One could rewrite your stack implementation to allocate memory for the node and for the data itself, thus freeing the need for client code to handle the memory in any special manner. It could look like:
int my_stack_push(struct my_stack *stack, void *data) {
...
// allocate memory for new link in the list
struct my_stack_node *nodeToPush = malloc(sizeof(struct my_stack_node));
if (nodeToPush == NULL) abort();
// allocate memory for data itself
nodeToPush->data = malloc(stack->size);
if (nodeToPush->data == NULL) abort();
// memory copy the data into the pointer
memcpy(nodeToPush->data, data, stack->size);
..
In such implementation the stack is responsible in freeing the pointer and stores only copies of the data. Thus all the handles need to be rewritten to support that. The size of the data is available via stack->size and initialized in my_stack_init with the argument.
Storing pointers value in a file seems like a bad idea from serialization point of view. Pointer values change in between runs. Storing pointers to list elements feels exactly bad. It's better to store the memory of the data itself, I see no reason in storing the pointer values of the list. Note that in current implementation the value of stackNode->next is indeed not used in my_stack_read, because it's value has been freed already before. I fail to see why do you even write the value of stackNode->next to the file if you never use it.
So we can store the data itself in the file:
int my_stack_write(struct my_stack *stack, char *filename){
...
node = stack->first;
void *data = node->data;
// write the data behind the node to the file
if (fwrite(data, stack->size, 1, file) != 1) {
return -100;
}
}
In a similar way we could rewrite my_stack_read:
struct my_stack *my_stack_read(char *filename, size_t size) {
...
struct my_stack *stack = stack_init(size);
...
void *newdata = malloc(stack->size);
while (fread(newdata, stack->size, 1, file)) {
my_stack_push(stack, newdata);
}
free(newdata); // as in my proposed implementation my_stack stores copy
// of the memory behind the pointers, we can safely manage own memory
// by freeing the pointer
}

Values change when printing from dynamicly allocated memory

Ok quick intro. I'm doing a homework on dynamically allocated memory. We need to simulate a CPU using structs and dyn. al. memory. I was testing if my stack functions properly and doesn't overflow. The stack is supposed to be 2 KiB, didn't overflow, but while printing the numbers, few addresses contain other numbers that i didn't put in. Ill just copy it here, and get rid of instruction lists, registers and such, that are not a problem and would make this long.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
struct cpu {
struct stack* memory;
};
struct stack {
int32_t* values;
int32_t* top;
};
void stackInit(struct stack* stack)
{
stack->values = malloc(2048);
stack->top = NULL;
}
void cpuInit (struct cpu* cpu)
{
stackInit(cpu->memory); //initialize stack
}
void stackPush(struct stack* stack, int32_t value)
{
if (stack->top == NULL){
stack->top = stack->values;
*(stack->top) = value;
}
else if (stack->top + sizeof(int32_t) < stack->values + 2048){
stack->top += sizeof(int32_t);
*(stack->top) = value;
}
}
void cpuDebug(const struct cpu* cpu)
{
int32_t* auxpt = cpu->memory->top;
if (cpu->memory->top != NULL)
for (int32_t i = 0; auxpt >= cpu->memory->values; i++){
printf("Value on the addr %d\n", *auxpt);
printf("Address of auxpt: %p\n", ( void * )auxpt );
auxpt -= sizeof(int32_t);
}
printf("\n");
}
int main()
{
struct cpu Cpu;
cpuInit(&Cpu);
for (int32_t i = 0; i < 550; i++){
stackPush(Cpu.memory,i);
}
cpuDebug(&Cpu);
return 0;
}
And the output looks like this :
Value on the addr 133
Address of auxpt: 0x562640529880
Value on the addr 10
Address of auxpt: 0x562640529870
Value on the addr 544108298
Address of auxpt: 0x562640529860
Value on the addr 2016419898
Address of auxpt: 0x562640529850
Value on the addr 1919181889
Address of auxpt: 0x562640529840
Value on the addr 128
Address of auxpt: 0x562640529830
Value on the addr 127
Any ideas why is this happening?
Thanks in advance
Man, you must allocate struct stack before access their members (values and top). You are accessing a non mallocated memory.
void stackInit(struct stack* stack)
{
stack = malloc(sizeof(struct stack));
stack->values = malloc(2048);
stack->top = NULL;
}
Following the tricks pointed in the comments, a better solution could be:
void stackInit(struct stack** stack)
{
(*stack) = (struct stack*)malloc(sizeof(struct stack));
(*stack)->values = (int32_t*)malloc(2048);
(*stack)->top = NULL;
}
void cpuInit(struct cpu* cpu)
{
stackInit(&cpu->memory); //initialize stack
}
In this way, the caller will see the allocated memory in the context of Cpu.memory.
Your struct cpu has an associated struct stack, but that stack is not part of the struct cpu; the cpu merely holds a pointer to a separate stack. You never initialize that pointer, nor indeed reserve any memory for the wanted stack. In particular, your cpuInit() function does not do this, and your stackInit() function depends on it already having been done.
Overall, supposing that each cpu requires only one stack for its lifetime, you would be better off making the stack an integral part of the cpu, so that you don't need to worry about such allocation issues:
struct cpu {
struct stack memory; // not a pointer
};
Having done that, you'll need to change the syntax with which you access the stack's members via the cpu, and you'll need to watch out for other semantic differences, but you can always get a pointer to the stack where needed (such as to pass to stackInit()) via the & operator.

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
}

Resources