linked list behavior - help me understand - c

i am in the process of learning linked lists, and i dont understand the behavior change when freeing a string. Here is the code:
#include <stdio.h>
#include <stdlib.h>
struct node {
char* data;
struct node* next;
};
void Push(struct node** headRef, char *data)
{
struct node* newNode = malloc(sizeof(struct node));
newNode->data = data;
newNode->next = *headRef; // The '*' to dereferences back to the real head
*headRef = newNode; // ditto
}
int main(int argc, const char * argv[])
{
char* auxStr;
struct node* list;
struct node* auxPtr;
int i=5;
while (i<9)
{
auxStr=malloc(sizeof("String:%d"));
sprintf(auxStr, "String:%d",i);
Push(&list, auxStr);
i++;
}
auxPtr=list;
i=0;
while (auxPtr)
{
printf("Node:%d - Data:%s\n",i++,auxPtr->data);
auxPtr=auxPtr->next;
}
return 0;
}
That results in:
Node:0 - Data:String:8
Node:1 - Data:String:7
Node:2 - Data:String:6
Node:3 - Data:String:5
now, when i add free(auxStr) in the first while:
while (i<9)
{
auxStr=malloc(sizeof("String:%d"));
sprintf(auxStr, "String:%d",i);
Push(&list, auxStr);
free(auxStr);
i++;
}
i now get:
Node:0 - Data:String:8
Node:1 - Data:String:8
Node:2 - Data:String:8
Node:3 - Data:String:8
Can someone explain why ? i know it may not be the most efficient code freeing in there multiple times, but i saw this behavio and it is puzzling me. Would appreciate your help to help me understand the concept better.
Thanks

You are getting undefined behavior.
You are freeing a memory (auxPtr) but you still happen to have a pointer to it - as the data in the relevant node. That is called a dangling reference.
What happens with this data is undefined, and it happens to be reusing the same address for each new allocation (but again, anything can happen).
Thus, when later printing the data - the output is undefined.

You aren't copying the data string by strncpy - instead you are simply assigning pointer to the same string that you free later

As indicated here acessing a poitner after you free it is undefined behavior.

Each of your data in struct node points on the same address. When freeing auxPtr, you are accessing to a memory location which is not allocated anymore. In C, it leads to an undefined behavior. It could be a better idea to dynamically allocate your data, as follow.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void Push(struct node **head, const char *data, size_t size)
{
struct node *elt;
elt = malloc(sizeof *elt);
assert(elt != NULL);
elt->data = malloc(size);
assert(elt->data != NULL);
memcpy(elt->data, data, size);
elt->next = *head;
*head = elt;
}
Moreover, there is no null pointer in your list. You should allocate list first.

Related

Why am I getting Segmentation fault (core dumped) or bus error (core dumped) when trying to populate a struct?

So I am trying to use a pointer to a struct of MonsterAttacks as the data that belongs to an element of a linked list. In order to do this I try to populate a struct of MonsterAttacks and then pass that along with a null ptr to a next node to a function called create. However somewhere in the populate method a segmentation fault error occurs. I am working with three files list_demo.c, linked_list.h and linked_list.c. I will build all the the functions that make up a fully functioning linked list, well hoping I can as soon as I get pass this error. Been dealing with this error for about two days and I showed my professor and he could not figure out why its happening, it seems to come from the populate function. I have tried to return a pointer to a strut in which case I get a bus error, and I have tried almost every variation of getting input and storing it on the strut. I even deleted the function and tried to populate it in main, but nothing works. I am new to C and my professor helped me out for about an hour debug this problem and he finally gave up, so any help would be appreciated.
list_demo.c
#include <stdio.h>
#include "linked_list.h"
#include <stdlib.h>
void populate(struct MonsterAttacks *m){
printf("Enter the name for the Monster \n");
scanf("%40s",m->monsterName);
puts("What is his/her attack location?");
scanf("%40s",m->attackLocation);
puts("What are the number of victims this monster has demolished?");
scanf("%ud", &m->numOfVictims);
m->attackID = 0;
}
int main(void)
{
node* tmp = NULL;
struct MonsterAttacks *tmpMonst = (struct MonsterAttacks *)
malloc(sizeof(struct MonsterAttacks));
if(tmpMonst == NULL){
printf("Error allocating memory");
}
else
populate(tmpMonst);
node *head = create(tmpMonst,tmp);
free(tmpMonst);
return 0;
}
linked_list.h
#ifndef LINKED_LIST
#define LINKED_LIST
typedef struct node{
struct MonsterAttacks *monsterAttack;
struct node* next;
} node;
struct MonsterAttacks{
unsigned int attackID;
char monsterName[41];
char attackLocation[41];
unsigned int numOfVictims;
};
/*
create a new node
initialize the data and next field
return the newly created node
*/
node* create(struct MonsterAttacks *m,node* next);
#endif
linked_list.c
// from zentut.com, heavily adapted
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "linked_list.h"
/*
create a new node
initialize the data and next field
return the newly created node
*/
node* create(struct MonsterAttacks *m,node* next)
{
node* new_node = (node*)malloc(sizeof(node));
if(new_node == NULL)
{
printf("Error creating a new node.\n");
exit(0);
}
new_node->monsterAttack->attackID = 0;
new_node->next = next;
strncpy(new_node->monsterAttack->monsterName,m->monsterName,41);
strncpy(new_node->monsterAttack->attackLocation, m->attackLocation, 41);
new_node->monsterAttack->numOfVictims = m->numOfVictims;
return new_node;
}
Btw running on Red Hat using gcc compiler
new_node->monsterAttack->attackID = 0;
Allocating memory for new_node does not allocate memory for the MonsterAttacks struct inside it. That is why dereferencing monsterAttack to get its attackID is causing a seg fault.
A minimal working code
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// Moved the two structs out to make a minimal reproducible code
/* #include "linked_list.h" */
struct MonsterAttacks{
unsigned int attackID;
char monsterName[41];
char attackLocation[41];
unsigned int numOfVictims;
};
typedef struct node{
struct MonsterAttacks *monsterAttack;
struct node* next;
} node;
void populate(struct MonsterAttacks *m){
printf("Enter the name for the Monster \n");
scanf("%40s",m->monsterName);
puts("What is his/her attack location?");
scanf("%40s",m->attackLocation);
puts("What are the number of victims this monster has demolished?");
scanf("%ud", &m->numOfVictims);
m->attackID = 0;
}
node* create(struct MonsterAttacks *m,node* next)
{
node* new_node = (node*)malloc(sizeof(node));
if(new_node == NULL)
{
printf("Error creating a new node.\n");
exit(0);
}
// Just add this line
new_node->monsterAttack = malloc(sizeof (struct MonsterAttacks));
new_node->monsterAttack->attackID = 0;
new_node->next = next;
strncpy(new_node->monsterAttack->monsterName,m->monsterName,41);
strncpy(new_node->monsterAttack->attackLocation, m->attackLocation, 41);
new_node->monsterAttack->numOfVictims = m->numOfVictims;
return new_node;
}
int main(void)
{
node* tmp = NULL;
struct MonsterAttacks *tmpMonst = (struct MonsterAttacks *)
malloc(sizeof(struct MonsterAttacks));
if(tmpMonst == NULL){
printf("Error allocating memory");
}
else {
populate(tmpMonst);
}
node *head = create(tmpMonst,tmp);
printf("Name: %s\n", tmpMonst->monsterName);
printf("num victim: %d\n", tmpMonst->numOfVictims);
free(tmpMonst);
return 0;
}
When you allocate memory for new_node in create(...), you allocate memory on the heap for a structure of type node to hold all the variables it contains. In this case, monsterAttack in node is initially a pointer to a struct that is pointing to nowhere. You need to explicitly allocate memory for the monsterAttack pointer to point to.
Edit: #bruceg pointed out the lack of semicolon, this malloc isn't the issue. #lightalchemist have highlighted that the second one is the fault.
struct MonsterAttacks *tmpMonst = (struct MonsterAttacks *);
malloc(sizeof(struct MonsterAttacks));
Your malloc call is wrong, malloc allocates and returns a pointer to the memory. You ignore/discard the pointer value.
Later code seems to assume that tmpMonst points to this allocated memory but there is no link between the two.
Try struct MonsterAttacks *tmpMonst = malloc(sizeof(struct MonsterAttacks));

Segmentation Fault with string compare

When working with a basic example like this, I am getting a segmentation fault. I believe it's due to the size of the data not being fixed. How can I have variable length data attached to a struct?
struct Node {
char * data;
struct Node* next;
};
void compareWord(struct Node** head_ref, char * new_data) {
if (strcmp((*head_ref)->data, new_data) > 0) {
head_ref->data = new_data;
}
}
int main(int argc, char* argv[]) {
struct Node* head = NULL;
head->data = "abc";
char buf[] = "hello";
compareWord(&head, buf);
return 0;
}
How can I have variable length data attached to a struct?
Answer is - No, you cannot. The reason is the size of the struct should be known at compile time.
The reason for segmentation fault is, your program is accessing head pointer before allocating memory to it:
struct Node* head = NULL;
head->data = "abc";
Allocate memory before using head:
struct Node* head = NULL;
head = malloc (sizeof(struct Node));
if (NULL == head)
exit(EXIT_FAILURE);
head->data = "abc";
Make sure to free allocated memory once you have done with it.
There is something known as Flexible Array Member(FAM) introduced in C99 standard. It may be of your interest.

C program acting weird

I've started implementing a circular queue in C, and I have the following lines of code:
#include <stdio.h>
#include <stdlib.h>
#include "cirq.h"
//allocate a circular queue
cirq cq_alloc(void){
cirq cq = NULL;
element *head;
element *tail;
if((head = malloc(sizeof(struct element*))) &&
(tail = malloc(sizeof(struct element *)))){
head->content = 0; // head node keeps track of size.
tail->content = NULL;
head->next = tail;
tail->next = head;
cq = &head;
} else {
printf("ERROR: No space for more cqueues.\n");
}
return cq;
}
int cq_size(cirq q){
return (int)(*q)->content;
}
int main(){
cirq q = cq_alloc();
printf("Size of element ptr %lu\n", sizeof(struct element *));
printf("%d\n", cq_size(q));
return 0;
}
Now when I compile and run this program, having commented out the line in main that prints out sizeof(struct element *)), the program runs fine and I get the right size of the queue, 0. When I leave the line in, the size of the struct is printed out, but after that I get a segmentation fault: 11. Also, to make things clear, the struct element has void *data and struct element *next fields. How can adding in a line that prints stuff change the behavior of the program so much?
EDIT: cirq.h
#ifndef CIRQ_H
#define CIRQ_H
typedef struct element **cirq; // cirq handle
typedef struct element {
void *content;
struct element *next;
} element;
extern cirq cq_alloc(void);// allocate a queue
extern int cq_size(cirq q);// return the size of a queue
extern void cq_enq(cirq q, void *value);// add a value to the queue
extern void *cq_deq(cirq q);// dequeue and return a queue value
extern void *cq_peek(cirq q);// return the value at the queue head
extern void cq_rot(cirq q);// requeue the head element at the tail
extern void cq_free(cirq q);// return all space allocated to queue
#endif
This is a bad smell:
if((head = malloc(sizeof(struct element*))) &&
You're mallocing the size of a pointer. I think you meant to malloc the struct itself...?
It doesn't really matter what cirq is, the fact that you return the address of a local object is the problem.
This here
cq = &head;
is causing the undefined behavior, because that's the address of the pointer head which is stored locally in the function only, when the function returns it's deallocated and thus invalid. Using it elsewhere (outside the function) is Undefined Behavior.
Also, do not typedef a pointer. Never do that, let the code reader know that it is a pointer.

Freeing a pointer (to a void*) inside of a struct

C newbie here, and I can't seem to figure this one out. So I'm starting to implement a linked-list (just something basic so I can wrap my head around it) and I've hit a snag. The program runs fine, but I can't free() the data stored in my struct.
Here's the source:
#include <stdio.h>
#include <stdlib.h>
struct node {
struct node* next;
void* data;
size_t data_size;
};
typedef struct node node;
node* create_node(void* data, size_t size)
{
node* new_node = (node*)malloc(sizeof(node));
new_node->data = (void*)malloc(size);
new_node->data = data;
new_node->next = NULL;
return new_node;
}
void destroy_node(node** node)
{
if(node != NULL)
{
free((*node)->next);
//this line here causes the error
free((*node)->data);
free(*node);
*node = NULL;
printf("%s\n", "Node destroyed!");
}
}
int main(int argc, char const *argv[])
{
float f = 4.325;
node *n;
n = create_node(&f, sizeof(f));
printf("%f\n", *((float*)n->data));
if (n->next == NULL)
printf("%s\n", "No next!");
destroy_node(&n);
return 0;
}
I get this message in the program output:
malloc: *** error for object 0x7fff5b4b1cac: pointer being freed was not allocated
I'm not entirely keen on how this can be dealt with.
This is because when you do:
new_node->data = data;
you replaces the value put by malloc just the line before.
What you need is to copy the data, see the function memcpy
node* create_node(void* data, size_t size)
...
new_node->data = (void*)malloc(size);
new_node->data = data;
Here, (1) you are losing memory given by malloc because the second assignment replaces the address (2) storing a pointer of unknown origin.
Number two is important because you can't guarantee that the memory pointed to by data was actually malloced. This causes problems when freeing the data member in destroy_node. (In the given example, an address from the stack is being freed)
To fix it replace the second assignment with
memcpy (new_node->data, data, size);
You also have a potential double free in the destroy_node function because the next member is also being freed.
In a linked list, usually a node is freed after being unlinked from the list, thus the next node shouldn't be freed because it's still reachable from the predecessor of the node being unlinked.
While you got an answer for the immediate problem, there are numerous other issues with the code.
struct node {
struct node* next;
void* data;
What's up with putting * next to type name? You are using it inconsistently anyway as in main you got node *n.
size_t data_size;
};
typedef struct node node;
node* create_node(void* data, size_t size)
{
node* new_node = (node*)malloc(sizeof(node));
What are you casting malloc for? It is actively harmful. You should have used sizeof(*new_node). How about checking for NULL?
new_node->data = (void*)malloc(size);
This is even more unnecessary since malloc returns void * so no casts are necessary.
new_node->data = data;
The bug already mentioned.
new_node->next = NULL;
return new_node;
}
void destroy_node(node** node)
{
if(node != NULL)
{
How about:
if (node == NULL)
return;
And suddenly you get rid of indenation for the entire function.
free((*node)->next);
//this line here causes the error
free((*node)->data);
free(*node);
*node = NULL;
printf("%s\n", "Node destroyed!");
What's up with %s instead of mere printf("Node destroyed!\n")? This message is bad anyway since it does not even print an address of aforementioned node.

C Stack pointing to Address?

I am new to C. I have implemented a simple stack with some structs and what not. I have posted the entire code below. The problem section is commented.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node *next;
} Node;
typedef struct Stack{
Node *top;
int size;
} Stack;
/* Function Prototypes */
void push(Stack *sPtr, int data);
int pop(Stack *sPtr);
void create(Stack *sPtr);
int main(void)
{
static Stack first;
create(&first);
push(&first,4);
push(&first,3);
push(&first,2);
printf("%d\n",pop(&first));
printf("%d\n",pop(&first));
printf("%d\n",pop(&first));
exit(1);
}
void push(Stack *sPtr, int data)
{
struct Node newNode;
newNode.data = data;
newNode.next = sPtr->top;
sPtr->top = &newNode;
sPtr->size++;
printf("%d\n",sPtr->top->data);
}
int pop(Stack *sPtr)
{
struct Node *returnNode = sPtr->top;
struct Node *topNode = sPtr->top;
if(sPtr->size != 0){
sPtr->top = topNode->next; /* =============PROBLEM?=============== */
return returnNode->data;
}
else{
printf("Error: Stack is Empty!\n");
return -1;
}
}
void create(Stack *sPtr)
{
sPtr->size = 0;
sPtr->top = NULL;
}
The output of this code is
4
3
2
2
8103136
680997
So obviously, it is pulling off the top node, and then printing the addresses of the next two nodes, instead of their data.
But why is it doing this? As far as I know (which is little) preforming this operation
sPtr->top = topNode->next;
should tell the program to make top now point to to topNode.next. But instead, it seems to be returning the address. What's going on here?
In your push() function, you're creating a new struct Node and adding it to your stack. However, the node is a local variable within the scope of push()--allocated on the stack (not your stack, the call stack), and will be gone when push() returns.
What you want to do is create the node on the heap, which means it will still be there after push() returns.
Since you're coding in C, you'll want to do something like:
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
Since you're now dealing with heap-allocated memory, you'll need to make sure that at some point it gets freed (somewhere) using free().
You're also not decrementing size as Jonathan has pointed out.
One trouble is that pop() never decrements size, so size is really 'number of elements ever pushed onto stack', not 'the number of elements in the current stack'.
int pop(Stack *sPtr)
{
struct Node *returnNode = sPtr->top;
struct Node *topNode = sPtr->top;
if (sPtr->size != 0)
{
sPtr->top = topNode->next;
sPtr->size--;
return returnNode->data;
}
else
{
fprintf(stderr, "Error: Stack is Empty!\n");
return -1;
}
}
Another trouble, as pointed out by unluddite in his answer is that you are not pushing data correctly. You need both fixes to be safe. There might still be other problems (such as not freeing memory correctly — or at all), but these two will get you a long way.

Resources