current uni student and i have been tasked with creating a stack using the following structure
typedef char stackitem;
struct stack {
stackitem d;
struct stack *next;
};
typedef struct stack ELEMENT;
typedef ELEMENT *POINTER;
I am having issues with printing my stack. What i am trying to do is create a temp stack where I print the element and then pop it off. however it seems to be removing it from the original stack which I dont want.
here is the code:
void print_stack(POINTER Top)
/*Print the contents of the stack. Do not modify the stack in any way. */
{
POINTER temp = Top;
printf("Start printing the stack ...\n");
while (temp != NULL){
printf("%c\n",temp->d);
pop(&temp);
}
}
void pop(POINTER *Top)
/* Remove the top item */
{
POINTER Top1 = *Top;
if (Top != NULL)
{
*Top = Top1->next;
// printf("Remove element %c\n", Top1->d);
free(Top1);
}
else
printf("Empty stack.\n");
}
In my mind I am printing and popping from the temp pointer which shouldn't affect the original pointer top but when i insert into the stack and print it the first time it prints correctly and then the second time it prints nothing.
this is my main
POINTER top;
top= (POINTER) NULL;
stackitem A='A';
stackitem B='B';
push(&top,A);
push(&top,B);
print_stack(top); // prints B A
print_stack(top); // prints nothing
Any help with what is going wrong?
Your print_stack() method currently looks like this:
void print_stack(POINTER Top) {
POINTER temp = Top;
printf("Start printing the stack ...\n");
while (temp != NULL){
printf("%c\n",temp->d);
pop(&temp); // Calling `pop()` will modify your Stack
}
}
You're calling pop() in your loop which modifies the stack; which is not good since print_stack is a read operation and should not be modifying the list. You can instead just increment the pointer so that it points to next value:
void print_stack(POINTER Top) {
POINTER temp = Top;
printf("Start printing the stack ...\n");
while (temp != NULL){
printf("%c\n",temp->d);
temp = temp->next; // Go to next element of stack
}
}
I tested this code(I just needed to add additional push and createNewStackNode methods that weren't present:
// ...
POINTER createNewStackNode(stackitem i) {
POINTER newNode = (POINTER) malloc(sizeof(ELEMENT));
newNode->d = i;
newNode->next = NULL;
return newNode;
}
void push(POINTER *top, char item) {
POINTER curr = createNewStackNode(item);
curr->next = *top;
*top = curr;
}
// ...
int main() {
POINTER top;
top= (POINTER) NULL;
stackitem A='A';
stackitem B='B';
push(&top,A);
push(&top,B);
print_stack(top); // prints B A
push(&top, 'C');
push(&top, 'D');
push(&top, 'E');
push(&top, 'F');
print_stack(top);
}
This prints the following output:
Start printing the stack ...
B
A
Start printing the stack ...
F
E
D
C
B
A
As another user suggested, you don't create a second stack rather you just create a second pointer that shows you the same memory position as the original stack.
If I was you I would try to add the stack into an array (you can search for guidance here) and then modify that array. That way your original stack will be unharmed.
The print_stack function claims it doesn't modify the stack, but it does. For example, it calls pop which calls free, destroying the object on the top of the stack.
Related
I've created a C header file for a data structure course which I'm taking in school. I've only limited coding experience in C and C++. It contains code which builds a stack using linked list for data storage. When I try to run a driver program with Visual Studio 2013 to test if the implementation works, it throws the following error:
HEAP CORRUPTION DETECTED: after Normal block (#68) at 0x006F8178.
CRT detected that the application wrote to memory after end of heap buffer.
The code in the said header file is listed below:
#include <stdlib.h>
#include <stdbool.h>
//type definition for a single stack data node
typedef struct node
{
void *dataPtr; //create void pointer to user data
struct node *link; //create pointer to next node in stack
}STACK_NODE;
//type definition for stack head structure
typedef struct stack
{
int count; //location to hold number of entries in stack
STACK_NODE *top; //create pointer to top of stack
}STACK;
//function to create empty stack
STACK* createStack()
{
STACK *stack; //create a stack head node
stack = (STACK*)malloc(sizeof(STACK));
if (stack){
stack->count = 0; //set stack count to zero
stack->top = NULL; //initialize top pointer to null
}
return stack; //return address of node in dynamic memory
}
//function to push data onto stack
bool pushStack(STACK *stack, void *dataInPtr)
{
STACK_NODE *newPtr;
newPtr = (STACK_NODE*)malloc(sizeof(STACK_NODE*));
//if out of memory
if (!newPtr)
return false;
newPtr->dataPtr = dataInPtr; //assign data pointer to node
newPtr->link = stack->top; //set link to point to node currently indicated as stack top
stack->top = newPtr; //set top node to point to data in new node
++(stack->count); //add one to stack count
return true;
}
//function to pop data off the stack and recycle node
void* popStack(STACK *stack)
{
void *dataOutPtr;
STACK_NODE *temp;
//if stack is empty, return NULL
if (stack->count == 0)
dataOutPtr = NULL;
else{
temp = stack->top; //set temp to point to top node to be recycled
dataOutPtr = stack->top->dataPtr; //set dataOutPtr to point to value currently stored in the top node
stack->top = stack->top->link; //set top pointer to point to next node in stack
free(temp); //delete top node
--(stack->count);
}
return dataOutPtr; //return address of popped data
}
//function to retrieve data in top node
void* stackTop(STACK *stack)
{
//if stack is empty, return NULL
if (stack->count == 0)
return NULL;
//if top node contains data, return dataPtr
else
return stack->top->dataPtr;
}
//function to test if stack contains data
bool emptyStack(STACK *stack)
{
return (stack->count == 0);
}
//function to delete nodes in stack
STACK* destroyStack(STACK *stack)
{
STACK_NODE *temp;
//if stack is not empty
if (stack){
//delete all nodes in stack
while (stack->top != NULL){
//delete data entry in top node
free(stack->top->dataPtr);
temp = stack->top; //set temp to point to top node to be recycled
stack->top = stack->top->link; //set top node to point to next node
free(temp); //destroy top node
}
//stack now empty, destroy stack head node
free(stack);
}
return NULL;
}
The code for the driver program is listed below:
#include <stdio.h>
#include <stdlib.h>
#include "stackADT.h"
int main()
{
int a = 4;
int *dataPtr, *result, *popped;
STACK *stack1;
//create stack
stack1 = createStack();
//push value in a onto stack and output value on stack top
dataPtr = malloc(sizeof(int));
*dataPtr = a;
pushStack(stack1, dataPtr);
result = (int*)stackTop(stack1);
printf("Value in stack is: %d\n", *result);
//pop stack and output popped value
popped = (int*)popStack(stack1);
printf("Value popped off is: %d\n", *popped);
destroyStack(stack1);
return 0;
}
TBH, when I first saw the error message I have no idea what it meant. After doing some rudimentary research I now understand that it is caused by writing data to the heap buffer without having allocated enough memory in the first place.
While I'm not entirely certain, I believe the error occurs both in the popStack and destroyStack function at the line:
temp = stack->top;
And it is detected and reported at the line:
free(temp);
My idea is to pass the address contained in the current top node (stack->top) to a temporary node and then call free() to release the memory in the temp node. Since both pointers are of the same type (i.e. both are of STACK_NODE type), I do not understand why the assignment action would trigger a heap corruption error.
Any help in resolving the issue will be greatly appreciated!
newPtr = (STACK_NODE*)malloc(sizeof(STACK_NODE*));
Is allocating enough storage for a "STACK_NODE Pointer" - normally 4 bytes - but sizeof (STACK_NODE) is 8 bytes, so when you use newPtr you are overwriting memory.
The correct form is
newPtr = (STACK_NODE*)malloc(sizeof(STACK_NODE));
I am currently working on stacks right now. I am supposed to use the following structures and function prototypes:
typedef struct node_{
char data;
struct node_ *next;
}node;
typedef struct stack_{
unsigned int size;
node* stack;
}stack;
stack* create_stack();
void push(stack* s, char val);
Here is my actual code for create_stack() and push():
stack* create_stack()
{
stack *stack;
stack = malloc(sizeof(stack));
stack->size = 0;
stack->stack = NULL;
return stack;
}
void push(stack* s, char val)
{
stack *newStack;
newStack = create_stack();
newStack->stack->data = val;
newStack->stack = s->stack;
s = newStack;
}
I am getting a segmentation fault when I try to store char val into newStack->stack->data. How does this not work? What do I need to do to make this stack on top???
The push function is wrong.
void push(stack* s, char val)
{
stack *newStack;
newStack = create_stack(); /* new stack created, why not work on the existing one ? */
newStack->stack->data = val; /* you're writing to a NULL pointer */
newStack->stack = s->stack;
s = newStack; /* this will not be visible from outside the function */
}
First of all, you are trying to recreate a new stack for each call of this function, which is certainly not what is intended.
If you try to modify the value of s, it will not be visible from outside the function, and you will still have your original stack.
Then, you are accessing the stack->data member even though stack has no space allocated to it yet (because you set it to NULL). You actually set it right after, which is why it crashes, most probably.
You probably want to do something like this:
void push(stack* s, char val)
{
node * n;
/* go to the end of the "stack" */
n = s->stack;
while (n != NULL) {
n = n->next;
}
/* allocate memory for a new node */
n = malloc(sizeof(node));
/* initialize node */
n->data = val;
n->next = NULL;
/* increment stack size */
s->size++;
}
And as mentionned before, this is merely a singly-linked list which is not the best fit for a stack, because as it exists now, you have to follow the node pointers to reach the last element, which makes push and pop operations O(N).
A faster implementation would look like this:
void push(stack* s, char val)
{
node * first_node, * new_node;
first_node = s->stack;
/* allocate memory for a new node */
new_node = malloc(sizeof(node));
/* initialize node */
new_node->data = val;
new_node->next = first_node;
/* increment stack size */
s->stack = new_node;
s->size++;
}
The top of the stack is always the first node, and the performance is O(1).
Follow your code....
stack *newStack = create_stack(); // in push()
newStack = malloc(sizeof(stack)); // in create_stack()
newStack->stack = NULL; // in create_stack()
newStack->stack->data = val; // in push()... this is where you crash.
Because newStack->stack is a NULL pointer. Your create_stack() function sets it to NULL, and you then dereference it. You have to allocate a struct node somewhere.
This code also has some readability issues which might be contributing to the problem. You are naming variables the same names as their types, which is very confusing. Consider using some other naming pattern like stack_t for types and stack for variable names.
I need to create the push method for a program that push an element into a stack. I have created this typedef:
typedef struct node{
int value;
struct node *next;
} Node;
With this snippet of code in my main:
Node *stackptr;
stackptr = NULL;
This is where I have a problem and am not sure exactly what is going on - In my push method im not sure if I am returning the updated pointer to the top of the stack. Im suppose to check if it is empty as well but I am going to get to that last. Here is the push() function:
void push(Node *stkptr, int i){
Node *temp;
temp = malloc(sizeof(Node));
temp->value = i;
temp->next = *stkptr;
return *stkptr = temp;
}
Hope this makes some sort of sense what I am trying to get across. Thanks for any advice you are able to give me. Hope all is well.
Last I am in need of fixing my int pop() function! I have to return the value of the node that was popped. I believe I am almost there - my compiler is still throwing errors. This is what I have so far:
int pop(Node** stkptr){
Node *temp;
temp = malloc(sizeof(Node));
if((*stkptr) == NULL){
fprintf(stderr, "The stack is empty. Pop is not allowed\n");
return 0;
}
else{
temp = *stkptr;
stkptr = *temp;
}
return stkptr;
free(temp);
}
However, the compiler is throwing the error:
incompatible types when assigning to type ‘struct Node **’ from type ‘Node’
warning: return makes integer from pointer without a cast
Can someone please help me fix my problem! Thanks!
There must be a lot of duplicates for this (for example, Implementing stack with linked list in C from the related questions section), but basically, you need to pass a pointer to a pointer into the function:
void push(Node **stkptr, int i)
{
Node *temp;
temp = malloc(sizeof(Node));
temp->value = i;
temp->next = *stkptr;
*stkptr = temp;
}
You also can't return a value from a function that returns void. You should also check that the memory allocation worked.
You'd call this from, for example, your main program:
Node *stack = NULL;
int i;
while (get_an_integer(&i) != EOF)
push(&stack, i);
where get_an_integer() is a hypothetical function that reads an integer from somewhere and assigns it to i, while returning a status (0 — got an integer; EOF — didn't get an integer).
An alternative design returns the new head of the stack from the function:
Node *push(Node *stkptr, int i)
{
Node *node;
node = malloc(sizeof(Node));
node->value = i;
node->next = stkptr;
return node;
}
with calling sequence:
Node *stack = NULL;
int i;
while (get_an_integer(&i) != EOF)
stack = push(stack, i);
A question about pop()
The pop() function appears to remove and destroy the first item on the stack, rather than returning it. However, there are a number of flaws in it, such as it allocates space, then overwrites the pointer with information from the stack, then returns before freeing the data. So, assuming that the demolition job is required, the code should be:
int pop(Node **stkptr)
{
assert(stkptr != 0);
Node *temp = *stkptr;
if (temp == NULL)
{
fprintf(stderr, "The stack is empty. Pop is not allowed\n");
return 0;
}
else
{
*stkptr = temp->next;
free(temp); // Or call the function to deallocate a Node
return 1;
}
}
This now returns 1 when successful and 0 when the stack was empty. Alternatively, if you wanted the value from the top of the stack returned rather than freed, then:
Node *pop(Node **stkptr)
{
assert(stkptr != 0);
Node *temp = *stkptr;
if (temp == NULL)
{
fprintf(stderr, "The stack is empty. Pop is not allowed\n");
return 0;
}
else
{
*stkptr = temp->next;
return temp;
}
}
Or, since you are told by the return value whether there was anything to pop, and printing in a library function can be objectionable, maybe even:
Node *pop(Node **stkptr)
{
assert(stkptr != 0);
Node *temp = *stkptr;
if (temp != NULL)
*stkptr = temp->next;
return temp;
}
Warning: none of the code has been submitted to a compiler for verification.
I am in an OS class and I have to write a simple stack program (the main function just determines what the user is asking you to do). If this were not required to be in C, I would have had this done ages ago, but because I am not very good at C coding, it has "a bug"... The bug so far is that it just continues to "pop" the same value off. (It's not actually popping anything off). I think it's because I don't understand how structures and pointers really work. Or is it a not so obvious coding mistake?
#include <stdio.h>
struct node {
int data;
struct node *next;
struct node *prev;
} first;
void push(int);
void pop();
int main(void)
{
int command = 0;
while (command != 3)
{
printf("Enter your choice:\n1) Push integer\n2) Pop Integer\n3) Quit.\n");
scanf("%d",&command);
if (command == 1)
{
// push
int num;
scanf("%d",&num);
push(num);
}
else
{
if (command == 2)
{
pop();
}
else
{
if (command != 3)
{
printf("Command not understood.\n");
}
}
}
}
return 0;
}
void push (int x)
{
struct node newNode;
newNode.data = x;
newNode.prev = NULL;
newNode.next = &first;
first = newNode;
printf("%d was pushed onto the stack.\n", first.data);
}
void pop()
{
if (first.data == '\0')
{
printf("Error: Stack Empty.\n");
return;
}
printf("%d was popped off the stack.\n", first.data);
first = *(first.next);
first.prev = NULL;
}
first should be a pointer. change it to struct node *first;
in main initialize first=NULL;
change you push/pop operations as below,
void push (int x)
{
struct node *newNode;// It should be a pointer
newNode = (struct node *)malloc(sizeof(struct node));
newNode->data = x;
//newNode.prev = NULL; // You don't need this
newNode->next = first;
first = newNode;
printf("%d was pushed onto the stack.\n", first->data);
}
void pop()
{
struct node *prevPtr;
//if (first.data == '\0')
if (first == NULL) // check if stack is empty
{
printf("Error: Stack Empty.\n");
return;
}
printf("%d was popped off the stack.\n", first->data);
prevPtr = first;
first = first->next;
free(prevPtr);
}
The problem is that first is a single global node, and it's the only node you ever have (aside from a temporary local node inside your call to push).
This line:
first = newNode;
just copies the contents of newNode over into first; and since newNode.next is pointing to first, this means that now first.next is pointing to first, so you have a single-element circular linked list.
Similarly, this line:
first = *(first.next);
just copies the contents of *(first.next) over into first; which is a no-op, since (due to the above), *(first.next) is first.
To solve this problem, you actually need to dynamically create nodes, using malloc (and free). And your global first variable should be a pointer — a node * — that always points to the top element of the stack. (Better yet, your push and pop functions should take first as an argument, rather than having this as a global variable. There's no need for these functions to only allow a single stack to exist.)
What's the value of &first? Hint, it's always the same since first is statically allocated. Even if you change the contents of the structure, the address won't change. This might tell you why there's a bug in push. You'll need to use malloc and free if you are going to have a structure of varying size.
When you have to manage memory yourself, as C requires you to do, you need to know the difference between areas of memory known as the stack and the heap. (This "stack" is slightly different than the data structure you are creating in your program.)
Your push() function is creating a new node on the stack; when the function exits the stack is popped and the memory occupied by your new node is up for grabs. The fact that you see values you entered is due to the fact that your program is very simple. If it was calling other functions that did other things they would almost certainly overwrite that part of the stack and when you called pop(), you would see garbage.
As others have indicated, you need to use the functions malloc() and free(), which give you memory from the heap instead of the stack.
If you want to make a stack with a linked list, make first variable as a pointer. then, when you push a new node to the stack, make a new node by allocating on the heap memory by malloc(). I know that you intend to use it to point to the top of the stack. right?
In your code, first variable is overwritten by a new node since it is not a pointer variable but a value variable. That makes a result to lost a top node of the stack.
void pop()
{
struct node *prevPtr;
//if (first.data == '\0')
if (first == NULL)
{
printf("Error: Stack Empty.\n");
return;
}
printf("%d was popped off the stack.\n", first->data);
prevPtr = first;
first = first->next;
free(prevPtr);
}
#include<stdio.h>
# define max 10
int stack[max],top=-1,size=0;
void push()
{
if(top==(max-1))
{
printf("stack full\n");
}
else
{
top++;
printf("enter the value which you want to insert\n");
scanf("%d",&stack[top]);
}
}
void pop()
{
int str;
if(top==-1)
{
printf("stack empty\n");
}
else
{
str=stack[top];
top--;
printf("the removed element is %d\n",str);
}
}
void display()
{
int i;
for(i=0;i<top;i++)
{
printf("%d\n",stack[i]);
}
}
void main()
{
int enter,x;
do
{
printf("enter 1 for push the element in the array\n");
printf("enter 2 for pop the element in the array\n");
printf("enter 3 for display the element in the array\n");
scanf("%d",&enter);
switch(enter)
{
case 1:push();
break;
case 2:pop();
break;
case 3:display();
break;
default:
printf("invalid syntax");
}
printf("for continue press 0\n");
scanf("%d",&x);
}
while(x==0);
}
I'm trying to destroy a single-linked-list, at first, my code of the destroy function like this:
void destroy_list_v0 (SLINK *list)
{
SLINK ptr = *list;
while (NULL != *list)
{
ptr = *list;
*list = (*list)->next;
free (ptr);
}
}
Function v0 performs perfect. here is output.
Input a number for length of the link.
init_start.
init_end & traverset_start.
The 0th element is 0.
The 1st element is 5.
The 2nd element is 93.
The 3rd element is 92.
The 4th element is 70.
The 5th element is 92.
traverse_end & destroy_start.
destroy_end & traverse_start.
traverse_end.
All operations done.
Then I thought that single ponter is enough, so I adjust the function into single pointer version:
void destroy_list_v1 (SLINK list)
{
SLINK ptr = list;
while (NULL != list)
{
ptr = list;
list = list->next;
free (ptr);
}
}
Here is v1's output:
Input a number for length of the link.
init_start.
init_end & traverset_start.
The 0th element is 0.
The 1st element is 27.
The 2nd element is 38.
The 3rd element is 20.
The 4th element is 66.
The 5th element is 30.
traverse_end & destroy_start.
destroy_end & traverse_start.
The 0th element is 0.
The 1st element is 32759808.
The 2nd element is 32759968.
The 3rd element is 32759936.
The 4th element is 32759904.
The 5th element is 32759872.
traverse_end.
All operations done.
To confirm that the destroy function is working fine, I traverse the linked list after it is destroyed. I found that the list could be read(in case of v0, it could not be read), though the value of every node has changed and indeterminacy. I thought after v0 performs, the pointer of the list point to NULL, but after v1 performs, it still point to original address. To test this idea, I adjust the v0 to v2:
void destroy_list_v2 (SLINK *list)
{
SLINK p_list = *list;
SLINK ptr = *list;
while (NULL != *p_list)
{
ptr = *p_list;
p_list = p_list->next;
free (ptr);
}
}
here is the v2 output:
Input a number for length of the link.
init_start.
init_end & traverset_start.
The 0th element is 0.
The 1st element is 76.
The 2nd element is 53.
The 3rd element is 80.
The 4th element is 31.
The 5th element is 97.
traverse_end & destroy_start.
destroy_end & traverse_start.
The 0th element is 0.
The 1st element is 13860864.
The 2nd element is 13861024.
The 3rd element is 13860992.
The 4th element is 13860960.
The 5th element is 13860928.
traverse_end.
All operations done.
I think my analysis is right, but it lead to new question.
The node struct is here:
typedef struct tag_node
{
int elem;
struct tag_node *next;
}NODE, *SLINK; //SLINK means SINGLE LINK
I have 2 questions:
1: The pointer 'next' is stored in the memory space which current pointer point to, after free current node, why the memory space of the pointer 'next' still could be read? Is the pointer 'next' still alive? I have this question because I thought that after v1 or v2 performs, it should be only the header node that could be read.
2: I thoutht v1 and v2 destroy the whole list, after v1 or v2 performs, why the value of header is still? I thought it should be like 1st to 5th that had changed to an indeterminate number.
Here is the whole code and the environment is Debian, clang:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define i_track(n) printf ("The %s's is %d.\n", #n, (n))
#define s_track(n) printf ("%s.\n", #n);
typedef struct tag_node
{
int elem;
struct tag_node *next;
}NODE, *SLINK; //SLINK means SINGLE LINK
void node_track (SLINK list);
NODE *node_generate (void);
SLINK init_list (int len);
SLINK locate_cur (SLINK list, int target_elem);
void insert_node (SLINK *list, int new_elem, int tag_elem);
SLINK traverse_list (SLINK list);
void clear_list (SLINK list);
void destroy_list_v0 (SLINK *list);
void destroy_list_v1 (SLINK list);
void destroy_list_v2 (SLINK *list);
void list_info (SLINK list, int node_elem);
int main (int argc, char *argv[])
{
int len;
SLINK list;
printf ("Input a number for length of the link.\n");
scanf ("%d", &len);
s_track(init_start);
list = init_list (len);
s_track(init_end & traverset_start);
traverse_list (list);
s_track(traverse_end & destroy_start);
// destroy_list_v0 (&list);
// destroy_list_v1 (list);
destroy_list_v2 (&list);
s_track(destroy_end & traverse_start);
traverse_list (list);
s_track(traverse_end);
s_track(All operations done);
return EXIT_SUCCESS;
} /* ---------- end of function main ---------- */
NODE *node_generate (void)
{
NODE *new_node = malloc (sizeof (NODE));
if (NULL == new_node)
{
printf ("ERROR: malloc failed.\n");
exit (EXIT_FAILURE);
}
memset (new_node, 0, sizeof(NODE));
return new_node;
}
SLINK locate_cur (SLINK list, int target_elem)
{
NODE *prev, *cur;
prev = node_generate ();
cur = node_generate ();
for (prev = list, cur = list->next; NULL != cur && target_elem != cur->elem; prev = cur, cur = cur->next)
;
return cur;
}
void insert_node (SLINK *list, int new_elem, int tag_elem)
{
NODE *new_node = node_generate ();
NODE *cur = *list;
new_node->elem = new_elem;
if ((int)NULL == tag_elem)
{
new_node->next = (*list)->next;
(*list)->next = new_node;
}
else
{
*list = locate_cur (cur, tag_elem);
new_node->next = (*list)->next;
(*list)->next = new_node;
}
}
SLINK init_list (int len)
{
SLINK header = node_generate ();
srand ((unsigned) time(0));
int elem;
for (int i = 0; i < len; i++)
{
elem = rand () % 100;
if (4 == elem / 10)
{
elem = elem + 50;
}
if (4 == elem % 10)
{
elem = elem + 5;
}
if (0 == elem % 100)
{
elem = elem + 999;
}
insert_node (&header, elem, (int)NULL);
}
return header;
}
void clear_list (SLINK list)
{
for (SLINK cur = list->next; NULL != cur; )
{
cur = cur->next;
free (list->next);
list->next = cur;
}
}
void destroy_list_v0 (SLINK *list)
{
SLINK ptr = *list;
while (NULL != *list)
{
ptr = *list;
*list = (*list)->next;
free (ptr);
}
}
void destroy_list_v1 (SLINK list)
{
SLINK ptr = list;
while (NULL != list)
{
ptr = list;
list = list->next;
free (ptr);
}
}
void destroy_list_v2 (SLINK *list)
{
SLINK p_list = *list;
SLINK ptr = *list;
while (NULL != p_list)
{
ptr = p_list;
p_list = p_list->next;
free (ptr);
}
}
SLINK traverse_list (SLINK list)
{
SLINK ptr = list;
for (int node_num = 0; ptr != NULL; ptr = ptr->next)
{
list_info (ptr, node_num);
++node_num;
}
return list;
}
void list_info (SLINK list, int node_num)
{
if (1 == node_num % 10 && 11 != node_num)
{
printf ("The %dst element is %d.\n", node_num, list->elem);
}
else if (2 == node_num % 10 && 12 != node_num)
{
printf ("The %dnd element is %d.\n", node_num, list->elem);
}
else if (3 == node_num % 10 && 13 != node_num)
{
printf ("The %drd element is %d.\n", node_num, list->elem);
}
else
{
printf ("The %dth element is %d.\n", node_num, list->elem);
}
}
void node_track (NODE *flag)
{
printf ("The flag element is %d.\n", flag->elem);
}
Freeing the memory is not the same thing as changing the address contained in the pointer variable.
The call to free releases the memory back to the heap managed by malloc. If you have a variable still pointing to the memory you had previously allocated, it is still pointing there after the free operation. However, it is a bug to use the pointer for anything after the free.
If you want to ensure your linked list does not still point to freed memory, you can assign NULL to each pointer in the structure after the associated memory has been freed.
Get used to this in C. The phrase "the behavior is undefined" is a mantra you will soon get used to, and it means doing certain things can lead to anything from a crash to apparently perfect behavior.
Pointers are a classic case of this mantra. You freed the memory and can still access it? Well, it's undefined. Wait, it's daylight savings time and now it crashed? Well it's undefined. Wait, you ran it on Windows and it works fine except on days ending in Y? Well it's undefined.
Remember the mantra; it will serve you well. Expecting C to complain loudly when you do something wrong is the wrong expectation, and keeping this in mind can save you much grief and tears.
Welcome to the land of C, where you can do anything (even if it's not legal). What you've done is invoked undefined behavior. You're not allowed to access the deleted memory anymore, but no one is stopping you from trying. C doesn't check that you are indeed accessing valid memory. It just goes ahead and does what you tell it to, even if you tell it to do something wrong. The fact that it "worked" is a mixture of luck and implementation defined stuff. Bugs like these are hard to find because instead of crashing, the program continues, and you don't find the bug until a long while later when it finally does start crashing. Once you invoke undefined behavior (which is what happens when you access deleted memory), anything can happen, from a black hole opening up and swallowing us all, to the program crashing, to the program appearing to work just fine.
v1 and v2 do destroy the whole list, but freeing memory doesn't mean you also erase the values in memory. The values are still there, you just no longer are allowed to access them because you've given those memory buckets back to the OS. The buckets still hold values. They're just not yours anymore.
free() marks the buffer as free, which means that subsequent malloc() could use the same date area, it doesn't mean that it will be erased or anything, but it could be returned to the operating system (and hence accessing it could cause a segmentation fault).
Accessing the freed memory is a bug because even if it usually is still there untouched, you could be accessing memory used by some other functions.
C99 says:
The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by the calloc, malloc, or realloc function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.