Freeing C Stack & Removing Dangling Pointers - c

I've implemented a stack in C, using a stackADT struct and a set of functions:
#ifndef _stack_h
#define _stack_h
// Macros
#define MaxStackSize 100
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// The type of element that may
// be stored in the stack
typedef int stackElementT;
// The stackADT represents the abstract type used to store
// the elements that have been pushed
typedef struct stackCDT
{
// A pointer to an array of elements
stackElementT* elements;
// Number of elements on the stack
int count;
// Number of elements we can push onto
// the stack before having to resize
int size;
}* stackADT;
// This function allocates and returns a new stack, which is
// initially empty... AKA - The Constructor
stackADT NewStack(void)
{
// The stack to return
stackADT stack;
// Instanitate a new stack
stack = (stackCDT*)(malloc(sizeof(stackCDT)));
// Start with 0 elements of course
stack->count = 0;
// Allocate memory for 50 integers
stack->elements = (stackElementT*)(malloc(50*sizeof(stackElementT)));
// Establish the size of the stack
stack->size = 50;
return stack;
}
/********* GETTER FUNCTIONS *********/
// Returns the number of elements currently pushed
// onto the stack
int StackDepth(stackADT stack)
{
return (stack->count);
}
// This function returns the element a the specified index in
// the stack, where the top is defined as index 0
stackElementT GetStackElement(stackADT stack, int index);
// Function to print contents of stack
void PrintStack(stackADT stack)
{
int i = 0;
printf("count = %d\nsize = %d\n",stack->count,stack->size);
for(i = (stack->count - 1); i >= 0; i--)
{
if((i%10 == 0) && (i != 0))
printf("\n");
printf("%d\t",*(stack->elements + i));
}
}
// Functions to determine if stack is empty or full
int StackIsEmpty(stackADT stack)
{
if(stack->count == 0)
return 1;
else
return 0;
}
int StackIsFull(stackADT stack)
{
if(stack->count == stack->size)
return 1;
else
return 0;
}
// This function pushes the specified element onto the stack
void Push(stackADT stack, stackElementT element)
{
// A temporary array that we may use later on
stackElementT* temp = NULL;
int oldCount = stack->count;
int i = 0;
// If the stack if full we need to do a
// a transfer, resize, and retransfer, then push
if(StackIsFull(stack))
{
// temp will be the same size as the old stack
temp = (stackElementT*)(malloc((oldCount)*sizeof(stackElementT)));
// Now we perform the transfer
for(i = 0; i < oldCount; i++)
{
*(temp + i) = *((stack->elements) + i);
}
// Free the old memory
free(stack->elements);
stack->elements = NULL;
// Recreate the stack with a 50% increase in size/capacity
stack->elements = (stackElementT*)(malloc((3*oldCount/2)*sizeof(stackElementT)));
// Re-establish the size
stack->size = 3*oldCount/2;
// Now we perform the transfer back
for(i = 0; i < oldCount; i++)
{
*((stack->elements) + i) = *(temp + i);
}
// Free the temp array and
// remove dangling pointer
free(temp);
temp = NULL;
// Now we push the element onto the stack
*((stack->elements) + oldCount) = element;
// Increase the count
stack->count = oldCount + 1;
}
// If the stack isn't full
else
{
*((stack->elements) + oldCount) = element;
stack->count = oldCount + 1;
}
}
// This function pops the top element from the stack and returns
// that value
stackElementT Pop(stackADT stack);
// This function frees the storage associated with the stack
void FreeStack(stackADT stack)
{
// Start by freeing the elements on the stack
// and remove dangling pointers
free(stack->elements);
stack->elements = NULL;
// Finally free the stack
free(stack);
stack = NULL;
}
#endif
Obviously I'm not completely finished (needs a pop function). My concern is with the bottom function (FreeStack). I tested the code below as such:
#include <stdio.h>
#include <stdlib.h>
#include "Stack.h"
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
int main(void)
{
stackADT stack;
int i = 0;
stack = NewStack();
PrintStack(stack);
for(i = 0; i < 60; i++)
{
Push(stack,i);
}
PrintStack(stack);
FreeStack(stack);
_CrtDumpMemoryLeaks();
return 0;
}
The _CrtDumpMemoryLeaks() function is for Visual Studio and it indicates if there is a memory leak. Apparently I've sealed off any leaks when calling the FreeStack(stackADT stack) function. However, the stack pointer still holds a memory address, which is the issue because the FreeStack function is supposed to free the memory pointed to by the stack variable and set it equal to NULL. This occurs within the function, but when I return to the main function during debugging, I see the memory address still there. What is it I'm missing here? If I'm able to release the memory, why can't I remove the dangling pointer?

You pass the stack to the function by value, instead of by address, modify the function to receive (stackADT *) and you'll be good to go.
Clarification: as Christian commented, the function call, and the use of stack will have to be changed as well of course (since now it's a pointer to pointer...)

You're passing that stackADT object (pointer) by value in your pop method:
void FreeStack(stackADT stack)
So stack refers to the local copy of that pointer. When you set that pointer = NULL, you modify it only within FreeStack. The main method has its own copy of said pointer, not pointing to NULL.

Related

Why does this code segfault on one machine but run fine on another?

This code segfaults on line 97 (according to gdb) on one machine (Linode) yet runs just fine on a different machine (personal) and I haven't really been able to figure out why. I tried ensuring that the heap was extended properly via sbrk but that still didn't seem to fix the issue. If anyone wouldn't mind explaining what I did wrong, I'd really appreciate it.
`
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#include <stdio.h>
#include <math.h>
typedef struct block // This is a node in a linked list representing various sections of memory
{
int size; // size of the block of allocated memory
int free; // whether the block is free and can be reallocated
struct block* next; // pointer to the next block in the linked list
char end[1]; // end represents the end of the header block struct
} block_t;
#define STRUCT_SIZE sizeof(block_t)
// --- Global variables
block_t* head = NULL; // Pointer to head of the linked list
block_t* lastVisited = NULL; // Pointer to the last visited node
void* brkPoint = NULL; // This is a pointer to the empty space on heap
// findBlock: Iterates through all blocks of memory until it is able to return a block able to contain a node of size size.
// headptr: Head of the linked list of blocks
// size: Size of the memory section being claimed
block_t* findBlock(block_t* headptr, unsigned int size) {
block_t* ptr = headptr;
while (ptr != NULL) {
if (ptr->size >= (size + STRUCT_SIZE) && ptr->free == 1) {
return ptr;
}
lastVisited = ptr;
ptr = ptr->next;
}
return ptr;
}
// splitBlock: Given a block ptr, split it into two blocks of size of size and ptr->size - size
// ptr: Pointer to the block being split
// size: Size of the first one of the two blocks
void splitBlock(block_t* ptr, unsigned int size) {
block_t* newBlock;
newBlock = ptr->end + size;
newBlock->size = ptr->size - size - STRUCT_SIZE;
newBlock->free = 1;
newBlock->next = ptr->next;
ptr->size = size;
ptr->free = 0;
ptr->next = newBlock;
}
// Increase amount of memory the program uses from the heap
// lastVisitedPtr: Pointer to the beginning of free heap (end of the program heap)
// size: The amount that you want to increase
block_t* increaseAllocation(block_t* lastVisitedPtr, unsigned int size) {
brkPoint = sbrk(0);
block_t* curBreak = brkPoint; //Current breakpoint of the heap
if (sbrk(size + STRUCT_SIZE) == (void*)-1) {
return NULL;
}
curBreak->size = (size + STRUCT_SIZE) - STRUCT_SIZE;
curBreak->free = 0;
curBreak->next = NULL;
lastVisitedPtr->next = curBreak;
if (curBreak->size > size)
splitBlock(curBreak, size);
return curBreak;
}
// malloc: A custom implementation of malloc, behaves exactly as expected
// _size: the amount of memory to be allocated into a block
// returns void*, a pointer to the block
void* mymalloc(size_t _size) {
void* brkPoint1; // New heap breakpoint
unsigned int size = _size;
int memoryNeed = size + STRUCT_SIZE; // Total size needed, including metadata
block_t* ptr; // ptr to new block
brkPoint = sbrk(0); // Set breakpoint to heap
if (head == NULL) { // If being run for the first time
if (sbrk(memoryNeed) == (void*)-1) { // If you cannot allocate enough memory, return null
return NULL;
}
brkPoint1 = sbrk(0); // Set new breakpoint to heap
head = brkPoint; // Head is at heap
head->size = memoryNeed - STRUCT_SIZE;
head->free = 0; // Head is no longer free
head->next = NULL; // No next
ptr = head; // Return pointer is head
printf("Malloc %zu bytes\n", size);
return ptr->end; // Return end of the metadata pointer (AKA beginning of allocated memory)
}
else { // Head exists
block_t* freeBlock = NULL;
freeBlock = findBlock(head, size); // Find a block that can fit size
if (freeBlock == NULL) {
freeBlock = increaseAllocation(lastVisited, size); // Increase heap and create new block
if (freeBlock == NULL) {
return NULL;
}
printf("Malloc %zu bytes\n", size);
return freeBlock->end;
}
else { // Free block with size > _size exists, claim it
if (freeBlock->size > size) { // If block's size is > size, split it down to size
splitBlock(freeBlock, size);
}
}
printf("Malloc %zu bytes\n", size);
return freeBlock->end;
}
}
// myfree: Sets block referenced by pointer to be free and merges consecutive blocks
void myfree(void* ptr) {
block_t* toFree;
toFree = ptr - STRUCT_SIZE;
if (toFree >= head && toFree <= brkPoint) {
toFree->free = 1;
printf("Freed %zu bytes\n", toFree->size);
}
}
#define ARRAY_ELEMENTS 1024
int main() {
// Allocate some data
int *data = (int *) mymalloc(ARRAY_ELEMENTS * sizeof(int));
// Do something with the data
int i = 0;
for (i = 0; i < ARRAY_ELEMENTS; i++) {
data[i] = i;
}
// Free the data
myfree(data);
return 0;
}
`
As mentioned above, I tried debugging with gdb and expanding the heap with sbrk, but that didn't fix the issue. I have no idea why it would run fine on my personal machine but not on a machine hosted elsewhere. Thanks a lot for checking this out
There is a ton of warnings which you should fix.
This one in particular is likely to cause crashes:
t.c:61:16: warning: implicit declaration of function ‘sbrk’.
Why is this likely to cause a crash?
Without a prototype, the C compiler is required (by the standard) to assume that the function returns an int.
On 32-bit platforms, this typically doesn't cause a problem because sizeof(int) == sizeof(void*) == 4.
But on 64-bit platforms sizeof(int) == 4 and sizeof(void*) == 8. Thus assigning void *p = sbrk(0); without a prototype may result in the pointer having only the low 4 bytes of the returned address; and that is likely to produce a crash when that pointer is dereferenced.
When I add missing #include <unistd.h> (where the prototype for sbrk is), the crash goes away.
In general you should always compile with -Wall -Wextra and fix resulting warnings. The compiler will often tell you about bugs, and save you a lot of debugging time.

Remove items less than 10 from the stack

Numbers are pushed onto the stack, you need to pop the full stack, after which, the stack in which elements less than 10 are removed. Example code when all elements of the stack are removed:
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <stdio.h>
#include <math.h>
#include <malloc.h>
struct kkk
{
float elem[15];
int top; // index of the top rlrment
};
struct kkk* st, * element; // pointers
void Init(struct kkk* st) // initialization
{
st->top = NULL;
}
void Push(struct kkk* st, float f) // push an item onto the stack
{
if (st->top < 15)
{
st->elem[st->top] = f;
st->top++;
}
else
printf("Stack full\n");
}
float Pop(struct kkk* st) // pop an item from the stack
{
float el;
if ((st->top) > 0)
{
st->top--;
el = st->elem[st->top];
return el;
}
else
{
printf("Stack is empty \n");
return 0;
}
}
float Vulychtop(struct kkk* st) // deleting the top of the stack
{
if ((st->top) > 0) {
return(st->elem[st->top - 1]);
}
else {
printf("Stack is empty!\n");
return 0;
}
}
int Gettop(struct kkk* st) // top element of the stack without delting
{
return(st->top);
}
int Isempty(struct kkk* st) // check
{
if ((st->top) == 0)
return 1;
else return 0;
}
void Vuvid(struct kkk* st) // Output of all elements
{
int i;
i = st->top;
if (Isempty(st) == 1) return;
do {
i--;
printf("%f\n", st->elem[i]);
} while (i > 0);
}
int main()
{
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
int i, n, k;
float znach;
element = (struct kkk*)malloc(sizeof(struct kkk));
Init(element);
printf("Enter the number of items in the stack \n");
scanf("%d", &n);
for (i = 0; i < n; i++) {
printf("Enter the number %d: ", i);
scanf("%f", &znach);
Push(element, znach);
}
printf("In stack %d elements \n", Gettop(element));
printf("\n");
Vuvid(element);
printf("Top element %f\n", Vulychtop(element));
do {
printf("The element to be removed %f, ", Pop(element));
printf("Items left in the stack %d \n", Gettop(element));
} while (Isempty(element) == 0);
return 0;
}
Result: https://i.stack.imgur.com/wLczr.png
I create a stack, after which I start entering numbers into it. With that, I'm fine. Next, I find the top element of the stack and pop it out. After that, I need to remove those numbers from the stack, the value of which is less than 10, and I manage to completely clear the stack one by one. Can't make a condition for this.
One approach would be to use another stack as temporary storage. In pseudo-code something like:
* create tmp-stack
* while org-stack isn't empty
* data = pop org-stack
* if (data >= 10) push data to tmp-stack
* while tmp_stack isn't empty
* data = pop tmp_stack
* push data to org-stack
* free tmp-stack
This can be implemented using the already existing function.
Better performance can be achieved by operating directly on the array holding the stacks data. Based on ideas from #SergeBallesta it may look like:
* write-index = 0;
* read-index = 0;
* while read-index < top
* if array[read-index] >= 10
* array[write-index] = array[read-index]
* write-index = write-index + 1
* read-index = read-index + 1
* top = write-index
It seems you are using an old MS compiler. For example the header <malloc.h> is not a standard C header. Instead you should use the header <stdlib.h>. Also neither declaration from the header <math.h> is used. So you may remove the inclusion of the header.
The function Gettop does not do what is written in the comment for the function
// top element of the stack without delting
Actually it returns the current value of the data member st->top that is how many elements are present in the stack.
On the other hand, the comment for the function Vulychtop
// deleting the top of the stack
is incorrect. The function does not delete an element from the stack because the value of the data member st->top is not decreased. Also such a function should not output any message. It is the function Pop that removes an element from the stack.
The function Vulychtop could be defined the following way
int Vulychtop( struct kkk *st, float *value )
{
if ( st->top != 0 ) *value = st->elem[st->top - 1];
return st->top != 0;
}
The function Pop also should not issue any message and return an element of the stack.
Also there is no great sense to declare the pointer element in the file scope. It could be declared in main.
The pointer st declared in the file scope
struct kkk* st, * element; // pointers
^^
is not used in the program.
Also there is no need to allocate an object of the type struct kkk dynamically.
You could just write in main
struct kkk element;
Init( &element );
To remove elements that are less than 10 from the original stack using the open interface of the stack you need an auxiliary stack.
For example
struct kkk st;
Init( &st );
while( !Isempty( element ) )
{
float value;
Vulychtop( element, &value ); // here I am using the function definition I showed above
if ( !( value < 10.0f ) ) Push( &st, value );
Pop( element );
}
while( !Isempty( &st ) )
{
float value;
Vulychtop( &st, &value ); // here I am using the function definition I showed above
Push( element, value );
Pop( &st );
}
You can yourself add messages in the while loops about what elements are popped, pushed or removed.
Pay attention that as you allocated an object of the type struct kkk dynamically you should free it before exiting the program
free( element );

Linked list in C with OMP : memory issues

I've adapted a very nice omp-parallelized code to perform numerical integration I found here.
However, some massif profiling revealed that there is some serious memory-leaking going on...
I guess this is related to how popping elements from the stack is handeled, where the top element on the stack is returned but not removed from the stack.
So I figured, I could easily add this missing feature as I've done this before in serial code.
But I'm obiously doing something wrong because I'm getting an "invalid pointer error" after a couple of "frees" ...
Since I'm completely new to OMP I figured, I could ask you guys for help.
I'm really sorry, that I wasn't able to cut done my "example" more.
But I think, that it's important to see how the author implemented the linked list and which datastructures.
I guess most of the code below may be skipped, it looks fine to me.
The problem arises when I try to "free" the data associated with the top element of the stack in the function "pop_stack" at the very end.
Popping elements in serial seems to work fine - inside the parallel section it doesn't.
Can you spot the mistake(s) ?
Note: This is incomplete code. It won't compile.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <omp.h>
#include <math.h>
int num_threads;
#define INITIAL_STACK_SIZE 128
/* the stack structure */
struct stack_s{
int el_count; /* count of elements on stack */
int el_size; /* size of an element */
int mem_reserve; /* allocated memory for stack */
void* elements; /* pointer to begin of stack */
};
struct myparams { double c1; double c1;};
typedef struct _work_t{
double a;
double b;
double rec; /* max recursion depth */
struct myparams * p;
} work_t;
typedef struct stack_s* stack_t;
void create_stack(stack_t* stack, int element_size);
int empty_stack(stack_t stack);
void push_stack(stack_t stack, void* element);
void pop_stack(stack_t stack, void* element);
double do_something(
double (*f)(double,struct myparams*), /* function to be called */
double a,
double b,
int rec, /*max. recursion depth*/
struct myparams* p);
static double myfun(double x,struct myparams* p){ return exp(-x*x)/p->a+log(x*pow(p->b,2.0));}
int main(int argc,char** argv)
{
num_threads=omp_get_num_threads();
double a=somevalue;
double b=somevalue;
struct myparams pinit;
pinit.c1=someval;
pinit.c2=someval;
double answer=0;
#pragma omp parallel
{
answer = do_something(myfun, xmin, xmax, 100, &pinit);
} /* omp parallel */
return 0;
}
double do_something(
double (*f)(double,struct myparams*), /* function to be called */
double a,
double b,
int rec,
struct myparams* p)
{
stack_t stack;
work_t work;
int ready, idle, busy;
/* prepare stack */
work.a = a;
work.b = b;
work.rec=rec;
work.p=p;
create_stack(&stack, sizeof(work_t));
push_stack(stack, &work);
double result = 0.0;
busy = 0;
#pragma omp parallel default(none) \
shared(stack, result,f,busy) \
private(work, idle, ready)
{
ready = 0;
idle = 1;
while(!ready )
{
#pragma omp critical (stack)
{
if (!empty_stack(stack)){
/* we have new work */
pop_stack(stack, &work);
if (idle){
/* say others i'm busy */
busy += 1;
idle = 0;
}
}else{
/* no new work on stack */
if (!idle){
busy -= 1;
idle = 1;
}
/* nobody has anything to do; let us leave the loop */
if (busy == 0)
ready = 1;
}
} /* end critical(stack) */
if (idle)
continue;
/* do some calculations using values saved in work and as well as the function f
along with the function parameters saved in myparams
-> estimate an error & save it to 'delta' */
if(rec <= 0 || fabs(delta) <= global_tolerance)
{
//error acceptable
#pragma omp critical (result)
result += somevalue_computed_above;
}
else // error not acceptable
{
//push 2 new work-elements to stack
//prepare 1st new elem.
work.a = some_new_a;
work.b = some_new_b;
work.rec=rec-1;
#pragma omp critical (stack)
{
push_stack(stack, &work);
//prepare 2nd new element
work.a = some_new_a2;
work.b = some_new_b2;
work.rec=rec-1;
push_stack(stack, &work);
}
}
} /* while */
} /* end omp parallel */
return result;
}
/******************************************
* create new stack
******************************************/
void
create_stack(
stack_t* stack, /* stack to create */
int element_size) /* size of a stack element */
{
int initial_size = INITIAL_STACK_SIZE;
/* allocate memory for new stack struct */
(*stack) = (stack_t) malloc(sizeof(struct stack_s));
if (!(*stack)){
fprintf(stderr, "error: could not allocate memory for stack.. Abort.\n");
exit(1);
}
/* allocate memory for stack elements */
(*stack)->elements = (void*) malloc(element_size * initial_size);
(*stack)->mem_reserve = initial_size;
if (!(*stack)->elements){
fprintf(stderr, "error: could not allocate memory for stack.. Abort.\n");
exit(1);
}
(*stack)->el_size = element_size;
(*stack)->el_count = 0;
}
/*****************************************
* check if the stack is empty
*****************************************/
int
empty_stack
(stack_t stack)
{
return stack->el_count <= 0;
}
/*****************************************
* push a element on stack
*****************************************/
void
push_stack(
stack_t stack, /* target stack */
void* element) /* element to push */
{
int i, new_reserve;
int log2_count;
/* check if we need more memory for stack */
if (stack->el_count >= stack->mem_reserve){
/* calculate new size for the stack
it should be a power of two */
for (i = stack->el_count, log2_count = 0;
i > 0;
i>>1, log2_count++);
new_reserve = 1 << log2_count;
/* reallocate memory for phase thread tables
and nullify new values */
stack->elements = (void *) realloc(stack->elements,
stack->el_size * new_reserve);
if (!stack->elements){
fprintf(stderr, "error: can't reallocate stack.. Aborting\n");
exit(1);
}
stack->mem_reserve = new_reserve;
}
/* now push the element on top of the stack */
memcpy((char*)stack->elements + stack->el_count*stack->el_size,
element, stack->el_size);
stack->el_count++;
}
/*****************************************
* pop an element from stack
* THIS IS WHERE I SUSPECT A MISTAKE !
*****************************************/
void pop_stack(
stack_t stack, /* target stack */
void* element) /* where poped el. should be stored */
{
if (stack->el_count <= 0){
fprintf(stderr, "error: trying to pop from empty stack.\n");
exit(2);
}
stack->el_count--;
memcpy(element,
(char*)stack->elements + stack->el_count*stack->el_size,
stack->el_size);
// try to remove last element from stack
// in original code there was no cleanup
struct _work_t *tmp = (struct _work_t*) stack->elements+stack->el_count;
printf("ncount:%d, foo:%f\n",stack->el_count+1,tmp->a);
free(tmp); //Works as long as el_count == 1 but fails if it becomes 2
}
So it looks like pop_stack() is only called inside some stack related critical region, so we can stop worrying about data races.
You identify this part of the code:
/*****************************************
* pop an element from stack
* THIS IS WHERE I SUSPECT A MISTAKE !
*****************************************/
void pop_stack(
stack_t stack, /* target stack */
void* element) /* where poped el. should be stored */
{
if (stack->el_count <= 0){
fprintf(stderr, "error: trying to pop from empty stack.\n");
exit(2);
}
stack->el_count--;
memcpy(element,
(char*)stack->elements + stack->el_count*stack->el_size,
stack->el_size);
// try to remove last element from stack
// in original code there was no cleanup
struct _work_t *tmp = (struct _work_t*) stack->elements+stack->el_count;
printf("ncount:%d, foo:%f\n",stack->el_count+1,tmp->a);
free(tmp); //Works as long as el_count == 1 but fails if it becomes 2
}
as the possible seat of the problem. So the push_stack() clearly copies stack->el_size bytes of the element to the stack, and pop_stack() copies them back again. That all looks wonderful. The last part of pop_stack() is, however, a bit of a puzzle...
struct _work_t *tmp = (struct _work_t*) stack->elements+stack->el_count;
printf("ncount:%d, foo:%f\n",stack->el_count+1,tmp->a);
free(tmp); //Works as long as el_count == 1 but fails if it becomes 2
stack->elements is a void* pointer to the first byte of the first element, so adding stack->el_count to that does not give you the address of the element that has just been popped, except when stack->el_count == 0 !! So tmp is set to a nonsense value, and tmp->a is also, therefore, nonsense. As for the free(tmp)... only when el_count == 0 (now) will the free() not fail, but it will destroy the stack.
Looking at the way the stack works does not suggest to me that the pop_stack() needs to do any "cleanup". If you think it does, then you need to reconsider what that really needs to be. It's possible that you are using the stack as a stack of pointers to "stuff"... but in any case, there's a little more work to be done.

C, Global Variable change isn't kept outside the function

In the pop() function i am trying to change the value of the Last global variable. It works fine in the push(n) function, while in the pop() one it changes it inside the function (verifying it with prints) but then it resets to the previous value just after leaving the method. Can't get my head around it.
#include "stack.h"
#include <stdio.h>
#include <stdlib.h>
int *Stack;
int Last = -1;
void make_empty( void ){
free(Stack);
Last = -1;
Stack = NULL;
//Stack = malloc(4);
return;
}
int is_empty( void ){
if (Last == -1)
return 1;
return 0;
}
int top( void ){
if (is_empty()) {
printf("La pila è vuota");
}
return Stack[Last];
}
int pop( void ){
if (is_empty()) {
printf("La pila è vuota");
return 0;
}
int temp = Stack[Last];
printf("last: %d\n", Last);
Stack = realloc(Stack, (--Last+1)*sizeof(int));
printf("last: %d\n", Last);
return temp;
}
void push( int n ){
Stack = realloc(Stack, (++Last+1)*sizeof(int));
Stack[Last] = n;
return;
}
void print_stack( void ){
printf("last: %d\n", Last);
for (int c=0; c<=Last; c++)
printf("%d ", Stack[c]);
printf("\n");
}
You're not allocating enough space for your stack.
At the start Last is -1. Then you push an element to the stack and allocate space:
Stack = realloc(Stack, ++Last*sizeof(int));
After the increment, Last is 0. So you're allocating 0*sizeof(int) == 0 bytes. You then write to Stack[Last] which doesn't exist. This invokes undefined behavior, which in your case manifests by causing a variable to change when you don't expect.
Since Last contains the last valid index, you want to add 1 to this to get the proper number of elements to allocate:
Stack = realloc(Stack, (++Last + 1)*sizeof(int));
You make a similar mistake when popping:
Stack = realloc(Stack, --Last*sizeof(int));
You also need to add 1 here:
Stack = realloc(Stack, (--Last + 1)*sizeof(int));
The implementation of the stack contains undefined behavior.
For example initially Last is equal to -1.
int Last = -1;
then in the push operation
void push( int n ){
Stack = realloc(Stack, ++Last*sizeof(int));
Stack[Last] = n;
return;
}
there is allocated memory of the size equal to 0 because ++Last is equal to 0. You may not change the memory allocated with the size equal to 0.
A similar problem exists for the method pop. When Last is equal to 0 then in this statement
Stack = realloc(Stack, --Last*sizeof(int));
the expression --Last is equal -1 that is converted to the maximum value of the type size_t due to the type of the operand sizeof(int).
You could write for example the method push the following way
void push( int n ){
Stack = realloc(Stack, ( ++Last + 1 ) *sizeof(int));
Stack[Last] = n;
return;
}
And in the pop method you could use
if ( Last == 0 )
{
free( Stack );
Stack = NULL;
}
else
{
Stack = realloc(Stack, ( Last *sizeof(int));
}
--Last;
Pat attention to this if statement
if ( Last == 0 )
{
free( Stack );
Stack = NULL;
}
when the stack is empty all allocated memory must be freed and Stack must be set to NULL.

C: Stack element overwritten by a function call

I'm doing a school assignment, I've I've run into 2 problems. I have to simulate stacks, with arrays.
My current code is as follows:
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int capacity;
int * array;
int size;
} stack_tt;
int pop(stack_tt * stack_p);
void push(stack_tt * stack_p, int value);
int top(stack_tt * stack_p);
stack_tt * newStack(void);
int empty(stack_tt * stack_p);
int main() {
stack_tt * myStack = newStack();
push(myStack, 123);
push(myStack, 99);
push(myStack, 4444);
while (!empty(myStack)) {
int value;
value = pop(myStack);
printf("popped: %d\n", value);
}
return 0; }
stack_tt * newStack(){
stack_tt * newS = malloc(sizeof(stack_tt) * 20);
(*newS).capacity = 1;
(*newS).size = 0;
return newS;
}
void push(stack_tt * stack_p, int value){
if ((*stack_p).size >= (*stack_p).capacity) {
(*stack_p).capacity*=2;
//realloc(stack_p, stack_p->capacity * sizeof(stack_tt));
}
(*stack_p).array = &value;
(*stack_p).size++;
}
int pop(stack_tt * stack_p){
(*stack_p).size--;
int fap = *(*stack_p).array;
return fap;
}
int empty(stack_tt * stack_p){
if ((*stack_p).size >= 1)
return 0;
return 1;
}
Fist of, when I call the line
while(!empty(myStack))
It changes the value in my array to 1.
secondly I'm not able to change individual values in my array, whenever I try things like:
(*stack_p).array[0] = value;
It doesn't know where in the memory to look.
I hope someone is able to help me out :)
There are a couple of problems with the code as I see it.
Lets take the push function where you do
(*stack_p).array = &value;
That will make the array structure member point to the local variable value, and once the function returns the variable cease to exist leaving you with a stray pointer and using that pointer will lead to undefined behavior.
The second problem with that code is that your stack will only be pointing (illegally) to the last element added.
You must allocate memory explicitly for array and use capacity to keep track of how much memory is allocated. The use size as an index into the allocated array for the pushing and popping. Something like
stack_tt * newStack(){
stack_tt * newS = malloc(sizeof(stack_tt)); // Only allocate *one* structure
newS->capacity = 0; // Start with zero capacity
newS->size = 0;
newS->array = NULL;
return newS;
}
void push(stack_tt * stack_p, int value){
if (stack_p->size + 1 > stack_p->capacity){
// Increase capacity by ten elements
int new_capacity = stack_p->capacity + 10;
int * temp_array = realloc(stack_p->array, new_capacity * sizeof(int));
if (temp_srray == NULL)
return;
stack_p->capacity = new_capacity;
stack_p->array = temp_array;
}
stack_p->array[stack_p->size++] = value;
}
int pop(stack_tt * stack_p){
if (stack_p->size > 0)
return stack_p->array[--stack_p->size];
return 0;
}
int empty(stack_tt * stack_p){
return stack_p->size == 0;
}
There is no need to allocate space for 20 structs of type stack_tt, you only need to allocate space for one:
stack_tt * newS = malloc(sizeof(stack_tt));
however you need to allocate space for elements of the struct member array:
newS->array = malloc( sizeof(int)*20);
newS->size = 0;
newS->capacity = 20;
now you can use the array member.
When you push a value to the 'stack', you shouldn't overwrite the array member with the address of the local variable, that doesn't make sense and will cause undefined behavior in addition of loosing the previously allocated memory. Instead simply assign the value to the member array, in the function push:
stack_p->array[stack_p->size] = value;
stack_p->size++;
Similarly when you pop an element, take the current element from the member array:
stack_p->size--;
int fap = stack_p->array[stack_p->size];
The rest of the functions and code should be fixed in the same manner.
You're code is good, but probably you didn't understand the usage of realloc:
//realloc(stack_p, stack_p->capacity * sizeof(stack_tt));
This function returns a pointer to the newly allocated memory, or NULL if the request fails.
The realloc (as the function suggests) takes the memory pointed by the pointer you pass, and copies that memory block in a new and resized block. So the right code should be.
stack_p->array = realloc(stack_p->array, stack_p->capacity * sizeof(stack_tt));
This other line is wrong:
(*stack_p).array = &value;
Change it with:
stack_p->array[stack_p->size] = value;
Another little suggestion, every (*stack_p). can be replaced by stack_p->, which is more elegant.
In the newStack() you're mallocing 20 structs which is kinda useless. You just need one.
Then you should malloc the array for the first time:
newS->array = malloc(sizeof(int));
newS->capacity = 1;

Resources