Linked list questions - c

Hey so i am trying to create a linked list. The following code segment opens the file for reading, which then gets passed into a function to take the string break it down and place it into a node which is suppose to be placed into the list in an appropriate location.
void print_list(struct vm_node *root);
int addNodeBottom(char *val, struct vm_node *head);
struct stock_item* setupNode(char *line);
int main(int argc, char * argv[]) {
struct vm vm;
struct menu_item menu_items[NUM_MENU_ITEMS];
struct vm_node *vmNode;
vmNode = malloc(sizeof(struct vm_node));
/* The UNUSED() function is designed to prevent warnings while your
* code is only partially complete. Delete these 4 function calls once
* you are using the data structures declared above in your own code */
UNUSED(argc);
UNUSED(argv);
UNUSED(vm);
UNUSED(menu_items);
if (argc != 3) {
printf("insuffcient arguments \n");
return EXIT_SUCCESS;
}
/*open stock file*/
char* fileName = argv[1];
FILE *file;
file = fopen(fileName, "r+");
char buf[256];
vmNode->next = NULL;
while (fgets(buf, sizeof buf, file) != NULL) {
addNodeBottom(buf,vmNode);
}
print_list(vmNode);
/* Test reason for reaching NULL. */
if (feof(file)) /* if failure caused by end-of-file condition */
puts("End of file reached");
else if (ferror(file)) /* if failure caused by some other error */
{
perror("fgets()");
fprintf(stderr, "fgets() failed in file %s at line # %d\n", __FILE__,
__LINE__ - 9);
exit(EXIT_FAILURE);
}
fclose(file);
return EXIT_SUCCESS;
}
the following function is how i have described the setupNode function.
struct stock_item* setupNode(char *line) {
struct stock_item *root;
root = malloc(sizeof(struct stock_item));
char *ptr;
const char del[2] = "|";
const char delm[2] = ".";
char *prices;
strcpy(root->id, strtok_r(line, del, &ptr)); // returns the ID and stores in in the root node.
strcpy(root->name, strtok_r(NULL, del, &ptr)); // returns the description and stores it in the root node.
strcpy(root->description, strtok_r(NULL, del, &ptr)); // returns the description and stores it in the root node.
prices = strtok_r(NULL, del, &ptr); // returns a string of the price for vm_item.
int dol = atoi(strtok(prices, delm));
int cent = atoi(strtok(NULL, delm));
root->price.dollars = dol;
root->price.cents = cent;
int quantity = atoi(strtok_r(NULL, del, &ptr)); // returns how many items are in stock.
root->on_hand = quantity;
return root;
}
This is the addNode function
int addNodeBottom(char *val, struct vm_node *head){
//create new node
struct vm_node *newNode = malloc(sizeof(struct vm_node));
if(newNode == NULL){
printf("%s", "Unable to allocate memory for new node\n");
exit(-1);
}
newNode->data = setupNode(val);
newNode->next = NULL; // Change 1
//check for first insertion
if(head->next == NULL){
head->data = newNode->data;
head->next = newNode;
}
else
{
//else loop through the list and find the last
//node, insert next to it
struct vm_node *current = head;
while (TRUE) { // Change 2
if(current->next == NULL)
{
current->next = newNode;
break; // Change 3
}
current = current->next;
};
}
free(newNode);
return 0;
}
and the printList function
void print_list(struct vm_node *root) {
while (root) {
printf("%s ", root->data->id);
root = root->next;
}
printf("\n");
}
Here is the typeDefs
#ifndef VM_TYPE
#define VM_TYPE
#define IDLEN 5
#define NAMELEN 40
#define DESCLEN 255
#define NUMDENOMS 8
#define UNUSED(var) (void)var
#define COIN_COUNT 20
#define DEFAULT_ONHAND 20
/* Type definition for our boolean type */
typedef enum truefalse
{
FALSE, TRUE
} BOOLEAN;
/* Each price will have a dollars and a cents component */
struct price
{
unsigned dollars,cents;
};
/* The different denominations of coins available */
enum denomination
{
FIVE_CENTS, TEN_CENTS, TWENTY_CENTS, FIFTY_CENTS, ONE_DOLLAR,
TWO_DOLLARS, FIVE_DOLLARS, TEN_DOLLARS
};
/* Each coin in the coins array will have a denomination (20 cents,
* 50 cents, etc) and a count - how many of that coin do we have on hand
*/
struct coin
{
enum denomination denom;
unsigned count;
};
/* The data structure that holds the data for each item of stock
*/
struct stock_item
{
char id[IDLEN+1];
char name[NAMELEN+1];
char description[DESCLEN+1];
struct price price;
unsigned on_hand;
};
/* The data structure that holds a pointer to the stock_item data and a
* pointer to the next node in the list
*/
struct vm_node
{
struct stock_item * data;
struct vm_node * next;
};
/* The head of the list - has a pointer to the rest of the list and a
* stores the length of the list
*/
struct vm_list
{
struct vm_node * head;
unsigned length;
};
/* This is the head of our overall data structure. We have a pointer to
* the vending machine list as well as an array of coins.
*/
struct vm
{
struct vm_list * item_list;
struct coin coins[NUMDENOMS];
char * foodfile;
char * coinsfile;
};
#endif
and the format of the text file that is being read in for parsing.
I0001|Coke |375 ml Can of coke |3.50|50
I0002|Pepsi |375 ml Can of pepsi |3.00|20
I0003|Lemon Cheesecake|A delicious, 1/8 size slice of cheesecake |4.00|10
I0004|Mars Bar |A delicious 50 g Mars Bar chilled just the way you like it.|3.00|20
I0005|Lemon Tart |A delicious lemon butter tart with a pastry based |3.75|12
The output when trying to print the list is complete garbage so any thoughts?

You have undefined behavior, because in addNodeBottom you make e.g. current->next point the new node you allocate, then you free the new node, so the pointer in current->next now point to unallocated memory.
Also, when setting up the first node (when head->next is NULL) then don't set the next pointer of head, let it be NULL. Instead to distinguish between an empty list or not, check for a non-null data field:
if (head->data == NULL)
{
// List is empty
}
else
{
// List is not empty
}
Other tips: There's no need to allocate a new node until you actually add it to the list. And the loop to find the last node in the list can be simplified to this:
vm_node *current;
for (current = head; current->next != NULL; current = current->next)
{
// Empty loop body
}
After the above loop current will be the last node in the list, and you can now allocate a new node.
If I would rewrite the addNodeBottom function (without modifying the function signature), it would look something like this (without any error handling):
int addNodeBottom(char *val, struct vm_node *head){
//create new node
stock_item *data = setupNode(val);
if (head->data == NULL)
head->data = data;
else
{
vm_node *current;
for (current = head; current->next != NULL; current = current->next)
;
current->next = malloc(sizeof(*current->next));
current->next->data = data;
current->next->next = NULL;
}
return 0;
}
Note: You must set vmNode->data = NULL before calling the above function for the first time, not only vmNode->next.

The main issue is because you are creating a new Node, storing data in it and then deleting the new node using free.
I understand your logic as it is now in the list so you do not need it anymore. But that is not the issue. In the list you merely put a pointer that points to the new node you created. You do not copy the new node in the list. You only put a pointer that points to the allocated memory for the node you created. If you free that part of the memory then it no longer exists and that part can be overwritten by any other application or yours. So it ends up being garbage.

Related

Inside a loop a linked list seems to be ok but outside it is empty

I just started learning C, and I'm lost trying to read cars and their manufacturing years from a text file. The program says it doesn't find any cars when I try it with a .txt file containing the following rows:
Lada 1976
Ferrari 2005
Suzuki 1985
Volvo 1963
Toyota 1993
Honda 2011
I did a similar linked list program which read an integer from a user to be added to the list, instead of a file. I think this is completely identical, except now the program reads from a file. This goes wrong somewhere with the pointer first (my best guess from debugging prints I added to several places) or it could be that I have somehow managed to change the nodes only inside the while loop (another guess). Could someone point out what is wrong here?
#include <stdlib.h>
#include <stdio.h>
typedef struct car {
char brand[15];
int year;
struct car *prev;
struct car *next;
} car;
void readCars(char *fname, car *newCar, car *first, car *last);
void printCars(car *ptr, car *first);
void freeMemory(car *ptr, car *first);
int main(int argc, char *argv[]) {
car *first = NULL; // a pointer to the first node of the linked list
car *last = NULL; // a pointer to the last node of the linked list
car *newCar; // a pointer for new nodes
car *ptr; // a pointer for iterating the linked list
if(argc != 2) {
printf("No filename provided.\n");
exit(0);
}
printf("Reading the file %s.\n", argv[1]);
readCars(argv[1], newCar, first, last);
printCars(ptr, first);
freeMemory(ptr, first);
printf("Program ended.\n");
return(0);
}
void readCars(char *fname, car *newCar, car *first, car *last) {
FILE *tiedosto;
char rivi[22];
if ((tiedosto = fopen(fname, "r")) == NULL) {
printf("Failed to open the file.\nProgram ended.\n");
exit(0);
}
while (fgets(rivi, 22, tiedosto) != NULL) {
if ((newCar = (car*)malloc(sizeof(car))) == NULL) { // allocate memory for a new node
perror("Memory allocation failure.\n");
exit(1);
}
sscanf(rivi, "%s %d", newCar->brand, &newCar->year); // set the car brand and age to the new node
newCar->next = NULL; // set the pointer to next node to NULL as there is no next node
newCar->prev = last; // set the pointer to the previous node to the previous last node (NULL if there was no previous)
if (first == NULL) {
first = newCar; // the new node is the only node so it is the first node
last = newCar; // the new node is the only node so it is also the last node
} else {
last->next = newCar; // the new node is next node of the previous last node
last = newCar; // the new node is now the last node
}
}
fclose(tiedosto);
printf("File read into a linked list.\n");
}
void printCars(car *ptr, car *first) {
if(first == NULL) {
printf("No cars found.\n");
} else {
ptr = first;
int count = 1;
while (ptr != NULL) {
printf("%d. car: %s from the year %d.\n", count, ptr->brand, ptr->year);
count += 1;
ptr = ptr->next;
}
}
}
void freeMemory(car *ptr, car *first) {
ptr = first;
while (ptr != NULL) {
first = ptr->next;
free(ptr);
ptr = first;
}
printf("Memory freed.\n");
}
The lines
if (first == NULL) {
first = newCar; // the new node is the only node so it is the first node
last = newCar; // the new node is the only node so it is also the last node
} else {
last->next = newCar; // the new node is next node of the previous last node
last = newCar; // the new node is now the last node
}
in the function readCars only modify the variables first and last in the function readCars. They do not modify the variables first and last in the function main.
Therefore, the variable first in the function main will stay NULL, so, as far as that function is concerned, the linked list stays empty.
In order to solve the problem, the function main should not pass copies of the variables first and last to the function readCars, but should rather pass pointers (i.e. references) to these variables. So you should change the parameters of the function readCars to the following:
void readCars(char *fname, car *newCar, car **first, car **last)
You should also change the lines quoted above to the following:
if ( *first == NULL ) {
*first = newCar; // the new node is the only node so it is the first node
*last = newCar; // the new node is the only node so it is also the last node
} else {
(*last)->next = newCar; // the new node is next node of the previous last node
(*last) = newCar; // the new node is now the last node
}
Also, the parameter newCar of the function readCars does not serve any purpose as a parameter. You are instead using it as a local variable, so you should declare it as such and remove it from the parameter list of the function.
EDIT: As pointed out in the comment below, you are doing the same thing with the parameter ptr in the functions printCars and freeMemory. You should remove that parameter from these functions too.
My solution is:
#include <stdlib.h>
#include <stdio.h>
typedef struct car {
char brand[15];
int year;
struct car *prev;
struct car *next;
} car;
void readCars(char *fname, car *newCar, car **first, car **last);
void printCars(car *ptr, car *first);
void freeMemory(car *ptr, car *first);
int main(int argc, char *argv[]) {
car *first = NULL; // a pointer to the first node of the linked list
car *last = NULL; // a pointer to the last node of the linked list
car *newCar; // a pointer for new nodes
car *ptr; // a pointer for iterating the linked list
if(argc != 2) {
printf("No filename provided.\n");
exit(0);
}
printf("Reading the file %s.\n", argv[1]);
readCars(argv[1], newCar, &first, &last);
printCars(ptr, first);
freeMemory(ptr, first);
printf("Program ended.\n");
return(0);
}
void readCars(char *fname, car *newCar, car **first, car **last) {
FILE *tiedosto;
char rivi[22];
if ((tiedosto = fopen(fname, "r")) == NULL) {
printf("Failed to open the file.\nProgram ended.\n");
exit(0);
}
while (fgets(rivi, 22, tiedosto) != NULL) {
if ((newCar = (car*)malloc(sizeof(car))) == NULL) { // allocate memory for a new node
perror("Memory allocation failure.\n");
exit(1);
}
sscanf(rivi, "%s %d", newCar->brand, &newCar->year); // set the car brand and age to the new node
newCar->next = NULL; // set the pointer to next node to NULL as there is no next node
newCar->prev = *last; // set the pointer to the previous node to the previous last node (NULL if there was no previous)
if (*first == NULL) {
*first = newCar; // the new node is the only node so it is the first node
*last = newCar; // the new node is the only node so it is also the last node
} else {
(*last)->next = newCar; // the new node is next node of the previous last node
*last = newCar; // the new node is now the last node
}
}
fclose(tiedosto);
printf("File read into a linked list.\n");
}
void printCars(car *ptr, car *first) {
if(first == NULL) {
printf("No cars found.\n");
} else {
ptr = first;
int count = 1;
while (ptr != NULL) {
printf("%d. car: %s from the year %d.\n", count, ptr->brand, ptr->year);
count += 1;
ptr = ptr->next;
}
}
}
void freeMemory(car *ptr, car *first) {
ptr = first;
while (ptr != NULL) {
first = ptr->next;
free(ptr);
ptr = first;
}
printf("Memory freed.\n");
}
The main difference is that readCars needs a pointer to pointer to first and last. In this way, when the functions ends, the value of first and last does not change to the initial value (in the case NULL).
Let me know if you've understood.

I cant find the error of my linked list( why is my head pointer moving?)

I have tried so many times to set my head pointer pointing to the first node. At first(in the empty list) it correctly points the first node. But after the first loop, the head pointer points to the newnode linked. Actually now Im quite unsure about my whole code as well.
int main(void){
struct library *head = NULL; //set the head pointer to NULL
int option;
printf("Enter the number:");
while((option = getchar())!= 9){
switch(option){
case '1':
{
char title[1000];
char author[1000];
char subject[1000];
printf("Enter title of the book you want to add:");
scanf("%s",title);
printf("Enter author of the book you want to add:");
scanf("%s",author);
printf("Enter subject of the book you want to add:");
scanf("%s",subject);
add_book(title,author,subject,&head);
printf("successful! and head pointer is pointing to %s\n",head->collection.title);
break;
}
}
}
void add_book(char title[],char author[],char subject[], struct library ** head){
struct library *current;
struct library *newnode = malloc(sizeof(struct library));
newnode->collection.title = title;
newnode->collection.author = author;
newnode->collection.subject = subject; // assigning value inside newnode
newnode->num_books = 0;
newnode->next = NULL; // assign NULL value to the end of newnod
//when the head is NULL which means when the list is empty
if(*head == NULL)
{
current = newnode;
*head = current;
return;
}
else
{
current = *head; //assign the first node to current pointer
//find the last node of the list
while(current->next != NULL)
{
current = current->next;
}
current->next = newnode; // link the last node to new node
return;
}
}
This is struct for this
struct book {
char* title;
char* author;
char* subject;
};
struct library {
struct book collection;
int num_books;
struct library* next;
};
The lifetime of char title[1000];, char author[1000];, and char subject[1000]; ends when execution reaches the end of the block inside case '1': { /* ... */ }. Once this happens, the pointers that were assigned in add_book become dangling pointers - pointing to invalid memory.
To remedy this, you must ensure the lifetime of your strings matches the lifetime of the structures that contain them. This can be done either by allocating enough space in the structure itself
struct book {
char title[1000];
/* etc. */
};
or by dynamically allocating enough space for a copy of each string. In any case you must copy the string to this memory (man 3 strcpy).
If it is available on your system, man 3 strdup does both steps of the second form at once. Otherwise, it is roughly the same as strcpy(malloc(strlen(source_string) + 1), source_string);.
Also note that the scanf specifier %s is as dangerous as gets when used without a field-width specifier (e.g., char buffer[1000]; scanf("%999s", buffer);), as it can potentially overflow your buffer.
An example program. Enter strings one-by-one, and terminate with EOF CTRL+D (Windows: CTRL+Z, RETURN).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct link {
char *string;
/* alternatively: char string[512]; */
struct link *next;
};
void add_link(struct link **root, const char *string) {
struct link *node = calloc(1, sizeof *node);
node->string = strdup(string);
/* alternatively: strcpy(node->string, string) */
if (*root) {
struct link *tail = *root;
while (tail->next)
tail = tail->next;
tail->next = node;
} else
*root = node;
}
int main(void) {
struct link *head = NULL;
while (1) {
char buffer[512];
if (!fgets(buffer, sizeof buffer, stdin))
break;
/* remove newline */
buffer[strcspn(buffer, "\n")] = '\0';
add_link(&head, buffer);
}
for (struct link *node = head, *next; node; node = next) {
next = node->next;
printf("STRING: %s\n", node->string);
free(node->string);
free(node);
}
}
Note: in a real program you should always check the return values of your memory allocating functions (malloc, calloc, strdup, etc.) as they can fail.

adding item to the end of linked list segfault

struct vehicle *add_vehicle(struct vehicle *v){
struct vehicle *newcar = (struct vehicle*)malloc(sizeof(struct
vehicle));
scanf("%s", newcar->regnro);
scanf("%s", newcar->model);
newcar->next = NULL;
if(v->next == NULL){
v->next =newcar;
}
else{
struct vehicle *current = v;
while(current->next != NULL){
current = current->next;
}
current->next = newcar;
}
return v;
}
I am trying to add vehicle to end of the list but it gives me Segmentation fault after second scanf, I dont know is my mistake in loops or in scanf. My struct is like:
struct vehicle {
char regnro[7];
char *model;
struct vehicle *next;
};
Firstly as pointed by other in comments model is pointer member of structure & its not initialzed. When you do scanf("%s", newcar->model); it gives seg. fault. So first allocate memory for it dynamically. For e.g
newcar->model = malloc(SIZE); /* define SIZE */
Secondly, you are passing just normal struct vehicle variable to add_vehicle() method So whatever changes are done with v in add_vehicle() won't affect in main() method. Instead pass the address of struct vehicle variable to add_vehicle() method, in that case you no need to return the struct variable as it will be call by reference. for e.g
main() method call looks like
struct vehicle *head = NULL;
add_vehicle(&head);
And definition of add_vehicle() looks like
void add_vehicle(struct vehicle **v){
/* some code */
}
Here is the sample code you may want
void add_vehicle(struct vehicle **v){
struct vehicle *newcar = malloc(sizeof(struct vehicle));/* no need to type cast the result of malloc */
scanf("%s", newcar->regnro);
/* model is pointer member of dtruct, you need to do malloc for it */
newcar->model = malloc(SIZE)); /*define the size value */
scanf("%s", newcar->model);
newcar->next = NULL;
if((*v) == NULL){ /* first node */
newcar->next = *v; /* newnode next make it to head node */
(*v) = newcar; /*update the head node and */
return;
}
else{
struct vehicle *current = *v;
while(current->next != NULL){ /* move temp to the end node of list */
current = current->next;
}
current->next = newcar;/* add the new_node at last of the list */
}
}
void print_info(struct vehicle *temp) {
while(temp) {
printf("%s %s\n",temp->regnro,temp->model);
temp= temp->next;
}
}
int main(void) {
struct vehicle *head = NULL;
add_vehicle(&head); /* pass the address of head */
print_info(head);
return 0;
}

How can I add items into a struct without creating variables

I am using a struct to store a string and an integer like so:
struct movement {
char *direction;
int steps;
};
I can add items into the struct by doing this
struct movement m1= { "right",20 };
struct movement m2= { "left" ,10 };
The end result I am trying to achieve is to collect user inputs (e.g. "right 20"), and store it in the struct. How can I store an unknown number of user inputs into the struct without the use of variables (m1, m2 etc) since I will not know how many items there will be at the end.
It doesn't sound as if you really want to "store values into the struct", instead you want to store a sequence of independent struct instances; one for each user input.
The three most basic ways of doing this are:
An array, whose size you select at compile-time. It shouldn't be too hard to make it "large enough" for reasonable input
An array whose size you set (and then grow) at run-time
A linked list of structure instances
Which one to prefer depends on which you think is simplest. If at all possible, a static appeoach is always simplest. You can easily have something like
struct movement movements[10000];
at the global level, this will only cost perhaps 120 KB on a 64-bit system. Note though that this doesn't include memory for the direction strings; if those are always chosen from "right" and "left" (and perhaps "up"/"down" too) you could represent it as an enum instead:
enum direction { DIRECTION_LEFT = 0, DIRECTION_RIGHT, DIRECTION_UP, DIRECTION_DOWN };
This will make the structure "self-contained" and (on 64-bit systems) smaller since the enumeration will be smaller than a pointer.
Dynamically growing an array using realloc() isn't too hard, you could look that up easily as its often used.
Use a linked list. It is a recursive data structure which would be great for what you want.
Here is some example code I wrote a while ago that might help:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* basic linked list structure */
typedef struct node node_t;
struct node {
char *direction;
int steps;
node_t *next;
};
/* pointers to the head and tail of the list */
typedef struct {
node_t *head;
node_t *foot;
} list_t;
list_t *initialize_list(void);
list_t *insert_nodes(list_t *list, char *direction, int steps);
void free_list(list_t *list);
node_t *generate_node(void);
void print_list(list_t *list);
void exit_if_null(void *ptr, const char *msg);
int
main(int argc, char const *argv[]) {
list_t *list;
/* empty list created */
list = initialize_list();
/* inserting information one a time */
list = insert_nodes(list, "right", 20);
list = insert_nodes(list, "left", 10);
print_list(list);
/* freeing list at the end */
free_list(list);
list = NULL;
return 0;
}
/* function to insert information into a node */
list_t
*insert_nodes(list_t *list, char *direction, int steps) {
/* called generate_node() to create a new node */
node_t *new;
new = generate_node();
/* puts steps information into node */
new->steps = steps;
/* allocates space for direction string */
/* this is needed because *direction is a pointer */
new->direction = malloc(strlen(direction)+1);
/* copies direction info into node */
strcpy(new->direction, direction);
/* inserting information at the tail of the list */
new->next = NULL;
if (list->foot == NULL) {
/* first insertion into list */
list->head = list->foot = new;
} else {
list->foot->next = new;
list->foot = new;
}
/* returns modified list */
return list;
}
.* function which generates new nodes */
node_t
*generate_node(void) {
node_t *newnode;
/* create space for new node */
newnode = malloc(sizeof(*newnode));
exit_if_null(newnode, "Allocation");
/* initialize node info to nothing */
newnode->direction = NULL;
newnode->steps = 0;
return newnode;
}
/* creates the empty linked list */
list_t
*initialize_list(void) {
list_t *list;
create space for list */
list = malloc(sizeof(*list));
exit_if_null(list, "Allocation");
/* set pointers to NULL */
/* We don't want them pointing at anything yet */
list->head = list->foot = NULL;
return list;
}
/* function which prints entire list */
void
print_list(list_t *list) {
/* start at the head of the list */
node_t *curr = list->head;
while (curr) {
printf("%s %d\n", curr->direction, curr->steps);
/* steps through the list */
curr = curr->next;
}
}
/* function which frees nodes */
void
free_list(list_t *list) {
node_t *curr, *prev;
/* start at beginning of list */
curr = list->head;
/* frees nodes one at a time */
while(curr) {
prev = curr;
curr = curr->next;
free(prev);
}
/* frees entire list */
free(list);
}
/* function which checks malloc(), and whether enough space was allocated */
void
exit_if_null(void *ptr, const char *msg) {
if (!ptr) {
printf("Unexpected null pointer: %s\n", msg);
exit(EXIT_FAILURE);
}
}
Use a LinkedList to store an indefinite number of movements.
For each movement, create a node in the linked list and update the next pointer.
struct node {
struct movement m;
node* next;
}

All Nodes in a linked list point to same object

The problem is somewhere in here....
char buffer[80];
char *name;
while (1) {
fgets(buffer, 80, inf); //reads in at most 80 char from a line
if (feof(inf)) //this checks to see if the special EOF was read
break; //if so, break out of while and continue with your main
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");//get first token up to space
stock = newStock(name,...)
....
}
I'm working in C with generic linked lists. I made a list implementation that I've tested and know works with chars. I'm trying to add stocks (I created a stock struct) to the linked list, with each node of the linked list holding a stock struct, but when I finish reading in the stocks all of the nodes point to the same struct and I can't figure out why. Here's some snippets of my code
list *list = malloc(sizeof(list));
newList(list, sizeof(stock_t));
while(1) {
...
(read from file)
...
stock_t *stock;
stock = newStock(name, closes, opens, numshares, getPriceF, getTotalDollarAmountF,getPercentChangeF,toStringF);
addToBack(list, stock);
}
Here's the newStock function:
stock_t *newStock(char *name, float closingSharePrice, float openingSharePrice, int numberOfShares, getPrice getP, getTotalDollarAmount getTotal, getPercentChange getPercent, toString toStr) {
stock_t *stock = malloc(sizeof(stock));
stock->stockSymbol = name;
stock->closingSharePrice = closingSharePrice;
stock->openingSharePrice = openingSharePrice;
stock->numberOfShares = numberOfShares;
stock->getP = getP;
stock->getTotal = getTotal;
stock->getPercent = getPercent;
stock->toStr = toStr;
return stock;
}
In a way I see what's wrong. newStock returns a new pointer every time, but it always gets stored in the variable 'stock' which is what every node points to, so it's going to be equal to whatever the last pointer newStock returned was...but I don't see the way around this. I tried having newStock return just a stock_t, and doing addToBack(list, &stock), but that didn't solve the problem either.
Any help would be appreciated!
Here is some code from the list:
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
Here's code from the stock struct:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef float (*getPrice)(void *S);
typedef float (*getTotalDollarAmount)(void *S);
typedef float (*getPercentChange)(void *S);
typedef char *(*toString)(void *S);
typedef struct stock{
char *stockSymbol;
float closingSharePrice;
float openingSharePrice;
int numberOfShares;
getPrice getP;
getTotalDollarAmount getTotal;
getPercentChange getPercent;
toString toStr;
}stock_t;
The generic functions probably seem like overkill but this is for homework (if you couldn't tell already) so we were asked to specifically use them. I don't think that has anything to do with the problem though.
Here are the definitions for those functions anyway
float getPriceF(void *S) {
stock_t *stock = (stock_t*)S;
return stock->closingSharePrice;
}
float getTotalDollarAmountF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice) * (stock->numberOfShares));
}
float getPercentChangeF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice - stock->openingSharePrice)/(stock->openingSharePrice));
}
char *toStringF(void *S) {
stock_t* stock = (stock_t*)S;
char *name = malloc(20*sizeof(char));
//sprintf(name, "Symbol is: %s. ", (stock->stockSymbol));
return stock->stockSymbol;
}
void printStock(void *S) {
char *str = toStringF(S);
printf("%s \n", str);
}
And this is how I'm traversing the list:
typedef void (*iterate)(void *); //this is in the list.h file, just putting it here to avoid confusion
void traverse(list *list, iterate iterator) {
assert(iterator != NULL);
node_t *current = list->head;
while (current != NULL) {
iterator(current->data);
current = current->next;
}
}
And then in my main I just called
traverse(list, printStock);
I can't find any problems with your code (that would cause your problem, anyway - there are places where you don't check the return from malloc() and stuff like that, but those are not relevant to this question). You don't supply the definition of stock_t, so I made a new data struct, and a new couple of functions, otherwise I just copied and pasted the code you provided:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* Your code starts here */
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
/* Your code ends here */
/* I made a new struct, rather than stock, since you didn't supply it */
struct mydata {
int num1;
int num2;
};
/* I use this instead of newStock(), but it works the same way */
struct mydata * newNode(const int a, const int b) {
struct mydata * newdata = malloc(sizeof *newdata);
if ( newdata == NULL ) {
fputs("Error allocating memory", stderr);
exit(EXIT_FAILURE);
}
newdata->num1 = a;
newdata->num2 = b;
return newdata;
}
/* I added this function to check the list is good */
void printList(list * list) {
struct node * node = list->head;
int n = 1;
while ( node ) {
struct mydata * data = node->data;
printf("%d: %d %d\n", n++, data->num1, data->num2);
node = node->next;
}
}
/* Main function */
int main(void) {
list *list = malloc(sizeof(list));
newList(list, sizeof(struct mydata));
struct mydata * data;
data = newNode(1, 2);
addToBack(list, data);
data = newNode(3, 4);
addToBack(list, data);
data = newNode(5, 6);
addToBack(list, data);
printList(list);
return 0;
}
which outputs this:
paul#MacBook:~/Documents/src$ ./list
1: 1 2
2: 3 4
3: 5 6
paul#MacBook:~/Documents/src$
demonstrating that you have a 3 node list, with all nodes different and where you'd expect them to be.
Either there is some other problem in code you're not showing, or for some reason you are thinking each node points to the same struct when it actually doesn't.
One possibility is that you have a char * data member in your stock struct. It's impossible to tell from the code you provided, but it's possible that you really are creating different nodes, but they all end up pointing to the same name, so they just look like they're the same. If you're assigning a pointer to name, you should make sure it's freshly allocated memory each time, and that you're not just, for instance, strcpy()ing into the same memory and assigning the same address to each stock struct.
EDIT: Looks like that was your problem. This:
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");
should be:
name = (char *) malloc(sizeof(char)*20);
....
strcpy(name, strtok(buffer, " "));
Right now, you malloc() new memory and store a reference to it in name, but then you lose that reference and your memory when you overwrite it with the address returned from strtok(). Instead, you need to copy that token into your newly allocated memory, as shown.

Resources