Passing a Structure to a Generic Linked List - c

I am tyring to use a generic linked list for my project. For reference, I am following this implementation made by David Muto. My aim is to parse user-information(user-name and other details) from a plain text file and load these into my linked-list.
typedef struct {
char *user_name;
char *password;
char *friends;
}user_info;
Here is the code-flow.
list users;
list_new(&users,sizeof(user_info*),free_users);
init_server(&users);
list_for_each(&users,iterate_users);
list_destroy(&users);
Parsing is done in init_server(). In it the user_info struct is allocated and reference to tokens are copied into it. After this I make a call to list_append().
list_append()
void list_append(list *list, void *element){
listNode *node = malloc(sizeof(listNode));
node->data = malloc(list->elementSize);
node->next=NULL;
memcpy(node->data,element,list->elementSize);
if(list->logicalLength == 0){
list->head = list->tail = node;
}else {
list->tail->next=node;
list->tail=node;
}
list->logicalLength++;
}
Problem
Only the username reference is present in all the elements in the list. Here are the values
of the element( structure reference passed to list_append()) and the head of the the list after the call returns.
(gdb) print *(user_info*)element
$31 = {username = 0x603270 "trudy", password =
0x603290"areyouthereyet",friends=0x6032b0 "bob;alice\n"}
(gdb) print *(user_info *)list->head->data
$36 = {username = 0x603270 "trudy", password = 0x0, friends = 0x0}

This:
sizeof(user_info*)
should be this:
sizeof(user_info)
You want the size of the struct itself, not the size of a pointer to it. Right now, your memcpy() call is not copying all the data as a result.
Similarly, listNode *node = malloc(sizeof(listNode)) should probably be listNode *node = malloc(sizeof(*node)) for the same reason. I'm assuming node->data = malloc(list->elementSize); will be correct once you're passing the right size to list_new().

What is the list->elementSize set to?
Another potential issue:
You're storing pointers to the memory where the strings are.
Suppose you parse a character string then store a pointer.
Later if the string is deallocated the pointer will be aimed at whatever gets placed there later. It will be very difficult to debug. Make sure your strings remain during the time you use your list.

Related

Storing a string in struct via an argument to a function

I'm struggling to figure out how to pass a string into a function and then how to store it in struct via pointer. I want to create a linked list where each node contains a node name(string) and data(integer) representing the weight required to reach that node.
The structure representing the node is as follows:
struct ListNode
{
// The energy required to reach in this node
int data;
// The name of the node
char location[20];
// And the nodes along side this one
struct ListNode* next;
struct ListNode* prev;
};
The following function generates a node and sets its next and previous pointers:
// Allocate a new listNode with the provided value
struct ListNode* listNodeConstructor(int value, char *city)
{
struct ListNode* newNode;
// Reserve memory for a node
newNode = malloc(sizeof(struct ListNode));
// Set its values
newNode->data = value;
newNode->name = strdup(city); /* This is how I've tried to implement it but it causes an error */
newNode->next = NULL;
newNode->prev = NULL;
// And return it
return newNode;
}
If anyone can show me how to correctly store the string in the node struct, I would be eternally grateful.
strdup() copies a string to a newly malloced place in the heap and returns a pointer to the new string.
Note that you also need to free it.
The problem is, that the string you want to set is part of the structure and not just a pointer you can set.
You have two options:
Use strcpy(newNode->name,city); instead of newNode->name = strdup(city);. This copies the city string to newNode but you need to assure that city has a \0 until newNode->name overflows.
change name to be just a pointer and free it when you free the node. You can use strdup in that case. (Change char location[20]; to char *location;
You cant assign arrays. You can only assign scalar variables or structs or unions
struct ListNode
{
// The energy required to reach in this node
int data;
// The name of the node
char *name;
char name1[32];
struct ListNode* next;
struct ListNode* prev;
};
int foo(struct ListNode *node, const char *str)
{
node -> name = str; // but remember that it only assigns the
//reference and does not copy the string
/* OR */
node -> name = strdup(str); // but remember to free it
strcpy(node -> name1, str);
}
You are attempting to use strdup (3) which creates a copy of the string in a newly heap allocated space. You are therefore attempting to assign a pointer (which is the return of your strdup to a char array. As you already have allocated your string space in your structure, you should therefore use strcpy (3) instead in the following way: strcpy(newNode->name, city).
Also please note it is always a good practice to pass pointer parameters as const when you do not intend to modify them. This convention aims to improve readability and is very helpful when you want to debug your programs when they grow bigger.

C - Appending new data to linked list changes the previous data, too

I am new to C and have stumbled upon a problem I hope you can help me with.
We have a double linked list
struct LinkedList {
LinkedListNode *first;
LinkedListNode *last;
};
and the nodes which are elements therein
struct LinkedListNode {
LinkedListNode *previous;
LinkedListNode *next;
char *data;
};
Trying to append elements to the list, I wrote a function which creates a node at the end of the list with the given data.
void append(LinkedList *list, char *data)
It works properly if I call the function like this:
append(list, "abc");
append(list, "def");
Which leads to the linked list containing 2 nodes - the first one with data being "abc" and the second one with data being "def".
However, if I pass the data as a variable, for example with
char testData[10] = "";
strcpy(testData, "abc");
append(list, testData);
strcpy(testData, "def");
append(list, testData);
The result is 2 linked list nodes both with the data "def".
The reason for it probably is that the data pointer inside the first and second node both point to the same memory, however, I don't know how to change this fact or how to circumvent it.
The code where I pass the data inside the function looks like this:
struct LinkedListNode *newNode = malloc(sizeof(struct LinkedListNode));
newNode->data = data;
I hope you can help me and thanks in advance.
The list is only storing a pointer to the data. In the first example those pointers to string literals are different, but in the second example, they all point to the same place, where the data is changing.
Perhaps you can try such as
char testData[10] = "";
strcpy(testData, "abc");
append(list, strdup(testData));
strcpy(testData, "def");
append(list, strdup(testData));
but remember that strdup allocates memory which will need to be freed later.
An alternative is to duplicate the string within the function, though will create redundancy if a string is passed that is already unique.
newNode->data = strdup(data);
Although the function strdup is in many libraries, it is non standard, but easy to create your own (thanks David Bowling).

C linked list can't properly add data to list

Bit of a lengthy question so please bear with me. I am trying to create a doubly linked list in C using a dummy node as the head. For whatever reason, however, the list only saves the last node I read into it, and links the prev node pointer and the next node pointer to that last node, so if I try and iterate over the list, it gets stuck in an infinite loop.
Here is my node header file and C file. The linked list implementation isn't meant to be a full linked list implementation, so I only included the functions I need:
node.h:
#ifndef _node_h
#define _node_h
#include "task_block.h"
#include <stdio.h>
typedef struct node {
task_block_type *data;
struct node *next;
struct node *prev;
}node_t;
node_t *node_new(task_block_type *data);
void add(node_t *new, node_t *head);
#endif
node.c:
#include "node.h"
#include "task_block.h"
#include <stdlib.h>
node_t *node_new(task_block_type *data) {
node_t *node = NULL;
node = malloc(sizeof(node_t));
node->data = data;
node->next = NULL;
node->prev = NULL;
return node;
}
void add(node_t *new, node_t *head) {
node_t *current = head;
if (head->next == NULL) {
head->next = new;
head->next->prev = head;
return;
}
while(current->next != NULL) {
current = current->next;
}
current->next = new;
current->next->prev = current;
return;
}
And finally, the code that is messing up from main.c:
while (j < numTasks) {
if (tasks[j].taskID == currentID) {
*newTask = *task_block_new(tasks[j].taskID, tasks[j].period);
newTask->startTime = starts[i];
newTask->deadline = deadlines[i];
newTask->executionTime = executions[i];
*nodeNew = *node_new(newTask);
add(nodeNew, eventQueue);
}
I have already tested that my new task_block_type get the correct data form the text file and that the new node I create is initialized properly with the task block. Once I read it into my list with add(), however, it messes up. Any help would be greatly appreciated as I've been trying to fix this problem for several hours now and still haven't found a solution
EDIT:
self contained example:
*node_new is meant to be a constructer for my node objects and is supposed to return a pointer to a node object. So for example, say instead of having a node which contains the task_block_type as above, I have one that contains an int. If I wanted to initialize it with a value of 5, I would call
*newNode = (node_t *)malloc(sizeof(node_t));
*newNode = *node_new(5);
Hope that helps
Change this:
*nodeNew = *node_new(newTask);
To this:
nodeNew = node_new(newTask);
Your original code copies the (dereferenced) value returned by node_new() to the value at (dereference of) *nodeNew. Thus, the pointer nodeNew never gets updated with the address of the new node created by node_new()... so you keep overwriting the value at *nodeNew while passing its unchanging address to add().
And you get a memory leak into the bargain. You are responsible for free()ing every pointer ever returned to you by malloc(). But here, for the same reason given above, you're not keeping copies of the returned pointers to enable this... just linking to nodeNew over and over again.
You need to update the pointer nodeNew with the location of, well, each new node, before passing it on to add(). Then you'll actually be linking different nodes, and at their original addresses, rather than copying them to the same address in a leaky fashion and linking it to itself, infinitely.
You also need to free() all memory that you have dynamically allocated once you're finished using it, e.g. through a sweep of the linked list in a 'destructor' function or at the end of your program. Otherwise you're leaking memory. This is a basic error and, even in cases where it doesn't stop a program from working, wastes users' RAM, which they rightly dislike!
I highly recommend studying pointers and dynamic allocation some more before continuing trying to write code like this.

Malloc of pointers in structs + pointer arithmetic + free() + linkedList

I'm trying to implement a linked-list data structure which each node has a identifier key, some data of variable length (malloc), and a pointer to the next node. Now I want to have 3 functions which respectively: sets a new node to the front of the list, prints the values of a given node using identifier key, and deletes a given node.
The struct I have for the node is as follows:
struct node {
char key[5];
int* data;
node* next;
};
struct node* headNode = NULL;
I have questions regarding each of functions. I will list the function codes I have and ask questions regarding that specific function below:
The code for my set function:
void command_set (char key[], int val[], int numOfVal){
struct node* temp = (node*)malloc(sizeof(node));
strcpy(temp->key, key);
temp->data = (int*)malloc(numOfVal*sizeof(int));
*(temp->data) = *(val);
temp->next = entry_head;
entry_head = temp;
return;
}
Now I have one question regarding this function:
1) Is my method of storing the data valid? i.e. "temp->data = (int*)malloc(numOfValuessizeof(int));" + "(temp->data) = *(val);". What I'm trying to do is dynamically allocate some memory, then store the given values as my node's data in that memory.
The code for my print function:
void printNode (char key[], int numOfVal){
int i;
struct node *currentNode = headNode;
while(currentNode->next!=NULL){
if(!strcmp(currentNode->key,key) ){
for(i=0; i<numOfVal; i++){
printf("%d ",*((currentNode->data)+i));
}
return;
}
currentNode = currentNode->next;
}
I have a one question regarding this function:
2) The data of a node is a list of integers, so does my way of printing out each integer actually work? i.e. "*((currentNode->data)+i)". What I'm trying to do is by using pointer arithmetic I print all the ints stored under data.
The code for my delete function:
void deleteNode (char key[]){
struct node *currentNode = headNode;
struct node *prevNode = headNode;
while(currentNode->next!=NULL){
if(!strcmp(currentNode->key,key) ){
prevNode->next = currentNode->next;
free(currentNode->data);
free(currentNode->next);
free(currentNode);
return;
}
prevNode = currentNode;
currentNode = currentNode->next;
}
I have two questions regarding this function:
3) Am I "deleting" the nodes properly? By using free(). Is this the way to do it?
4) Is this how you link up nodes after deletion? By setting the next pointer to another node.
Please assume that malloc will not return NULL for simplicity. Also note that I have simplified my actual code, else there is way too much to post, so there might be slight errors. You may also assum that the while loops will always work (i.e. there will not be a case where (currentNode->next==NULL). The main point of this post are my questions regarding whether the method of doing something is correct.
An example of the program would be:
-set ex1 2 3 4 5
-get ex1
2 3 4 5
-set ab 32 112
-get ab
32 112
Thanks in advance.
strcpy(temp->key, key);
For the the purpose of your program, this is probably ok, but you should use strncpy(temp->key,key,5) to be safe. Or at least check the length of key to make sure it fits.
*(temp->data) = *(val);
This only sets the first index in the array. You should use memcpy here.
memcpy (temp->data,val, sizeof (int) * numOfVal);
Your print function prints the first element that doesn't match. Did you mean to do the opposite?
Your delete function does the thing. It finds the first node that doesn't match.
You also don't want to free currentNode->next;

How to initialize a Linked List with a struct with many variables

I still have troubles with the relations between the linked lists and the structures.
See, my objectif is to create a list where each node contains 2 characters strings. So, I tried something like this : first, I create a structure that represent an element with my 2 char ; second, a control structure for my list, thath will point at the beginning of my list. Which, in my .h, gives something like this :
typedef struct s_def { char *first_word; char *second_word; struct s-def *next; } t_def
typedef struct s_type { t_def *first; } t_list;
Next, I try to initialize my list. I make a function that work like this :
t_list *list;
t_def *words;
list = malloc(sizeof(*list));
words = malloc(sizeof(*words));
if (list == 0 || words == 0)
return (NULL);
words = NULL;
words->next = NULL;
list->first = words;
return (list);
Precision : I try to make an empty list for now, so that the user can add some elements later.
And that's where it block : when I run the program, it gives the typical Segmentation Fault. But it don't see what's wrong with what I made ! I put some write in my function to retrace the process : the malloc are working ok, as well as the words = NULL, but then the segment fault seems to run at the line
words->next = NULL;
What do I make wrong ? Why can't I give a NULL value at the next of my words ?
You first initialize the word pointer with allocated memory
words = malloc(sizeof(*words));
Then 3 lines down you set that pointer to NULL again, creating a memory leak
words = NULL;
And then you try to dereference the pointer that you just set to NULL:
words->next = NULL;
So, just remove the words = NULL;
The problem is most likely this part:
words = NULL;
words->next = NULL;
Here you reassign the pointer words to be a null pointer, and directly afterwards you dereference this null pointer, leading to undefined behavior.
When you set words to NULL, you have made a null pointer. Trying to access it immediately afterwards by words->next is effectively doing NULL->next which will cause an error.
Your code looks a little more complex than it needs to be for a simple linked list implementation, you might try something like:
typedef struct s_element
{
char* firstWord;
char* secondWord;
s_element* next;
} t_element;
t_element* list = NULL;
t_element* addFront(t_element* list, char* word1, char* word2)
{
t_element* next = list;
list = malloc(sizeof(t_element));
if (!list) return NULL;
list->firstWord = word1;
list->secondWord = word2;
list->next = next;
return list;
}
Assuming I haven't made any bone-headed syntax mistakes, this should be about as clear as a linked list can get. Notice that it doesn't need to check if the list is empty, the only conditional is in case malloc has failed.

Resources