Failing While loop when working with Linked lists - c

As part of an assignment, I am having a program read in the names and id of people from a file, and saving them in separate linked lists. I have a nested while loop to traverse the list until it reaches the end, and then assign the values at the end. Unfortunately the program keeps crashing while reading and it seems to revolve around the second while loop.
Here is the struct established before the main()
struct node
{
char name[50];
int id;
struct node *next;
}*start;
Here is the function itself:
void read()
{
int tempNum = 0, id, jack = 0;
char name[50];
struct node *temp, *right;
temp = start;
FILE *ifp = fopen("AssignmentOneInput.txt", "r");
while(fscanf(ifp," %s", &name) != EOF)
{
fscanf(ifp, " %[^,]s, %[^/n]d", &name, &id);
temp = (struct node *)malloc(sizeof(struct node));
strcpy(temp->name,name);
temp->id = id;
right = (struct node *)start;
printf("test number one\n");
while(right->next != NULL)
{
printf("test number two\n");
right = right->next;
}
printf("test number three\n");
right->next = temp;
right = temp;
right->next = NULL;
}
}
As for its implementation in the main function, it is the very first function called, so it looks a little like:
main()
{
read();

I guess thatstart is a global variable? Then it will be zero-initialized by the compiler and C runtime system. That means it will, as a pointer, be initialized to NULL. So when you use it uninitialized in your code it will be NULL and you have undefined behavior when you dereference the pointer. And undefined behavior is arguably the most common cause of crashes.

Related

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.

Save (aka push) a pointer to a local variable to the linked list leads to Segfault [duplicate]

This question already has an answer here:
Why do i get the same string value but different arithmetic values when printing from a Doubly Linked List
(1 answer)
Closed 2 years ago.
This program takes multiple file names as command-line arguments and finds out the number of words in each file and how many times each word appears (i.e., the frequency) in all files. Specifically, the program will first determine the number of files to be processed. Then, the program will create multiple threads (one for each file). Each thread will count the number of words for the given file. In addition, each thread will access a global linked-list and update the number of times each word appears in all files.
However, I cannot print the word in each node. When I tried:
printf("%s appears %d times\n", node->word, node->count);
I got a segmentation fault.
Thread 1: number of words in File:input_file_1.txt is 6
Thread 2: number of words in File:input_file_2.txt is 14
All 2 files have been counted and the total of 20 words found !
Segmentation fault: 11
There's something wrong when I push nodes in linked list or print the linked list but I couldn't figure it out. Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
struct thread_info {
pthread_t thread_id; /* ID returned by pthread_create() */
int thread_num; /* Application-defined thread # */
char * filename; /* From command-line argument filename */
};
// A linked list node
struct Node{
char * word;
int count;
struct Node * next;
};
struct Node * head = NULL;
static int totalWordCount = 0;
void push(struct Node **head, char * new_data){
struct Node * new_node = (struct Node * ) malloc(sizeof(struct Node));
struct Node *last = *head;
new_node->word = new_data;
new_node->count = 1;
new_node->next = NULL;
if (*head == NULL) {
*head = new_node;
return;
}
while (last->next != NULL)
last = last->next;
last->next = new_node;
return;
}
bool search(struct Node **head, char * x){
struct Node * current = *head; // Initialize current
while (current != NULL){
if (strcmp(current, x) == 0){
current->count++;
return true;
}
current = current->next;
}
return false;
}
// This function prints contents of linked list starting from head
void printList(struct Node *head){
struct Node *node = head;
while (node != NULL){
printf("%s appears %d times\n", node->word, node->count);
node = node->next;
}
}
void * processFile(void * vargp){
int numberofwords = 0;
// Store the value argument passed to this thread
struct thread_info * tinfo = vargp;
FILE * fp;
fp = fopen(tinfo->filename, "r"); // read mode
if (fp == NULL){
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
char word[100];
while (fscanf(fp, "%s", word) != EOF) {
if (search(&head,word)){
} else{
push(&head, word);
}
numberofwords+=1;
}
printf("Thread %d: number of words in File:%s is %d\n", tinfo->thread_num, tinfo->filename, numberofwords);
totalWordCount += numberofwords;
fclose(fp);
}
int main(int argc, char const * argv[]){
pthread_t thread_id;
char ch, file_name[25];
int numberoffile = argc-1;
for (size_t i = 0; i < numberoffile; i++){
struct thread_info tinfo;
tinfo.thread_num = i + 1;
tinfo.filename = argv[i + 1];
pthread_create( & tinfo.thread_id, NULL, processFile, & tinfo);
pthread_join(tinfo.thread_id, NULL);
}
printf("All %d files have been counted and the total of %d words found !\n", argc - 1, totalWordCount);
printList(head);
//printf("%s appears %d times\n", head->word, head->count);
return 0;
}
Thank you so much!
There are several problems in your code. The problem that causes the seg fault is most likely that you save (aka push) a pointer to a local variable to the linked list.
In processFile function:
char word[100]; <---------------------- local variable
while (fscanf(fp, "%s", word) != EOF) {
if (search(&head,word)){
} else{
push(&head, word); <----------- call push
In push function:
void push(struct Node **head, char * new_data){
struct Node * new_node = (struct Node * ) malloc(sizeof(struct Node));
struct Node *last = *head;
new_node->word = new_data; <------- save pointer
A local variable like char word[100]; has automatic storage duration. This means that the variable only exists while you are inside that function (or inside functions called from that function). But once processFile returns, the variable word is automatically destroyed and it's not valid to access the memory anymore. In other words - your list contains pointers to invalid memory.
What you need to do is to save the word in some other memory.
For that you have at least two options.
Option 1: Change the node definition and use strcpy. Like:
struct Node{
char word[100];
int count;
struct Node * next;
};
and inside push do:
assert(strlen(new_data) < 100);
strcpy(new_node->word, new_data);
Option 2: Use dynamic allocation for the words in the linked list. Like:
void push(struct Node **head, char * new_data){
struct Node * new_node = malloc(sizeof(struct Node));
struct Node *last = *head;
new_node->word = malloc(1 + strlen(new_data));
assert(new_node != NULL);
strcpy(new_node->word, new_data);
But there are more problems with your code. For instance:
strcmp(current, x) ---> strcmp(current->word, x)
^^^^^^^ ^^^^^^^^^^^^^
not a string this is the string to compare
And here:
pthread_create( & tinfo.thread_id, NULL, processFile, & tinfo);
pthread_join(tinfo.thread_id, NULL);
you create a thread and then immediately joins it. That actually means that you only have one thread running at the time!
The code should have one loop for creating threads and another loop for joining them.
However, if you create/join correctly the multi-thread implementation will fail big time because all threads would read and modify the linked list at the same time.
You would need to add mutex protection so that only one thread can operate on the list. But if you do that, the threads will block each other all the time so there would be no real benefit from having multiple threads.
What this really means is that your overall design is problematic. It's not a good idea to have multiple threads using the same global linked list.
If you want to do this multi-threaded, each thread should have it's own list while adding words from the file. Then you would need some code to merge the lists once the file reading completes. I'm not sure it's worth the trouble but you can give it a try.
Finally, I don't think a linked list is a good data container for this application. The search will be slow (i.e. O(N)) and consequently performance be bad for large files (aka many words). Consider using a search-tree or some hash based table instead.

Problem using free() in a loop creating a linked list from a file

So I have a file called file.txt and i want to create a linked list from the information it contains, where each line of the file is a new node. So far I have this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct sAirport{
char name;
int number;
struct sAirport *next;
}tAirport;
tAirport *createNode(tAirport *newNode, char str[1000]);
void createLinkedList(tAirport **head, tAiport *newNode);
int main()
{
FILE *fa = fopen("test.txt", r);
char str[1000] = {0};
tAirport *head = NULL;
tAirport *newNode = NULL;
while(fgets(str, sizeof(str), fa) != NULL)
{
newNode = createNode(newNode, str);
createLinkedList(&head, newNode);
free(newNode);
newNode = NULL;
}
return 0;
}
tAirport *createNode(tAirport *newNode, char str[1000])
{
char *aux = NULL;
newNode = malloc(sizeof(tAirport));
if(newNode == NULL)
exit(EXIT_FAILURE);
aux = strtok(str, " ");
strcpy(&newNode->name, aux);
aux = strtok(NULL, " ");
sscanf(aux, "%d", &newNode->number);
newNode->next = NULL;
return newNode;
}
void createLinkedList(tAirport **head, tAirport newNode)
{
tAirport *temp = NULL;
if(*head == NULL)
{
*head = newNode;
return;
}
temp = *head;
while(temp->next != NULL)
temp = temp->next;
temp->next = newNode;
}
I'm getting weird results and Valgrind says I have lost bytes but I don't know what to do.
Edited so that it can run.
For example the file I'm testing with is:
John 33
Mary 42
Peter 12
What should I do?
Aside from all those warning you will get from compiling this. I just want to tell you that you are misunderstanding how malloc(),free(), and pointer work.
First of all, pointer is just an unsigned long, a natural number just like any other number. The difference is that the pointer store the address of the real memory ( in this case is newNode).
In your program, you malloc() to get your memory, asisgn the memory address to newNode, then you tell your list to hold newNode, finally you free it. So you just free the memory you wish to keep, your list now only hold a bunch of address to freed memory.
Solution for this is, get rid of free() while populating your list, and free them later
The sAirport structure is define the name to be one character. However, from the code, looks like the createNode will allow long name (up to 999 characters). When the createNode create the new entry, the strcpy will overwrite data beyond the allocated space, and will likely cause segmentation fault, or "funny" data.
Consider extending name to the proper size, or using dynamic allocation (malloc) for name.

trying to copy a c string in a struct using only C

I'm trying to insert a hard coded string into a char array value in a struct using only C, so I used memcpy, following the example in another post. But for some reason, I keep getting what looks like an address as output, I'm not sure why.
my console prints out: [ (2,7532592) (1,7524424) ] and other long numbers like that every time. I've checked so many examples on how to copy a sequence of characters into a c string, and it seems like this one was exactly the same. I might just be having trouble understanding pointers. Im not sure why it's spitting out the address value. Can anyone point out what I'm doing wrong? I apologize for any lack of knowledge on my part. My shortened down code is below:
struct node
{
int key;
char month[20];
struct node *next;
};
struct node *head = NULL;
struct node *current = NULL;
//display the list
void printList()
{
struct node *ptr = head;
printf("\n[ ");
//start from the beginning
while(ptr != NULL)
{
printf("(%d,%d) ",ptr->key,ptr->month);
ptr = ptr->next;
}
printf(" ]");
}
//insert link at the first location
void insertFirst(int key, char * month)
{
//create a link
struct node *link = (struct node*) malloc(sizeof(struct node));
link->key = key;
memcpy(link->month, month, 20);
link->month[19] = 0; // ensure termination
//point it to old first node
link->next = head;
//point first to new first node
head = link;
}
int main() {
insertFirst(1,"Jan");
insertFirst(2,"March");
printf("Original List: ");
//print list
printList();
}
You are printing the pointer ptr->month, not the actual string.
Try: printf("(%d,%s) ",ptr->key,ptr->month); (%s instead of %d).
Try
printf("(%d,%s) ",ptr->key,ptr->month);
instead for the "curious output" problem.

Segmentation Fault when using Linked Lists and Char arrays

I'm struggling through my programming homework and need a hand with Linked Lists. Basically we have to create a program that contains certain functions to manage linked lists. Fairly standard. I tried it last week and just couldn't get it. I'm trying to get it working this weekend and was making good progress until I ran into segmentation faults.
struct STUDENT
{
char *FirstName;
char *LastName;
char *PUID;
int age;
struct STUDENT *next;
};
This is the structure I'm trying to use. The first three values are char arrays and the fourth is just a number. Following this I tried to declare a starting and current node globally.
struct STUDENT *head = NULL;
struct STUDENT *curr = NULL;
Following this I have my create node function, which takes a user input and puts it into the list.
void *createListNode()
{
char first[MAXNAME];
char last[MAXNAME];
char ID[MAXID];
char *pfirst;
char *plast;
char *pID;
int tage;
struct STUDENT *temp = (struct STUDENT *) malloc (sizeof(struct STUDENT));
printf("Enter a first name: ");
scanf("%s", first);
pfirst = first;
printf("entered name: %s\n", pfirst);
printf("Enter a last name: ");
scanf("%s", last);
plast = last;
printf("entered name: %s\n", plast);
printf("Enter the Purdue ID: ");
scanf("%s", ID);
pID = ID;
printf("ID: %s\n", pID);
printf("Enter an age: ");
scanf("%d", &tage);
printf("age: %d\n", tage);
temp->FirstName = strdup(first);
printf("first\n");
temp->LastName = strdup(last);
printf("last\n");
temp->PUID = strdup(ID);
printf("id\n");
temp->age = tage;
printf("age\n");
temp->next = NULL;
printf("next\n");
if (curr == NULL)
{
printf("inside if\n");
head->next = temp; //-------SEGMENTATION FAULT---------------------
printf("line 107\n");
head = curr = temp;
printf("line 109\n");
}
else
{
curr = temp;
}
}
Background of what I'm done up to this point:
I was getting segmentation faults when I tried to assign my arrays to my 'temp' node, but by using malloc on it I solved that problem. Using print statements, I've tracked the problem to the indicated line. When I try to run the code, I get a segmentation fault. I tried the same malloc code above on the 'head' and 'curr' nodes, but that gave me:
carlton#carlton-Inspiron-N7010:~/CNIT315$ gcc lab5.c
lab5.c:20:64: error: invalid initializer
struct STUDENT head = (struct STUDENT *) malloc (sizeof(struct STUDENT));
This is where I believe the problem is, but I've been searching and experimenting for several hours and haven't gotten anywhere. And I just realized this is now a very long post. Thanks for reading it through and I appreciate any push in the right direction!
You initialize head to NULL. You can't dereference head before assigning a valid pointer to it. head->next = temp; gives you a segfault because head is a null pointer.
Thus, you need to review this part of the code:
if (curr == NULL)
{
printf("inside if\n");
head->next = temp; //-------SEGMENTATION FAULT---------------------
printf("line 107\n");
head = curr = temp;
printf("line 109\n");
} else
{
curr = temp;
}
Since it seems like you want to insert at the end of the list, I would suggest something like this:
temp->next = NULL;
if (head == NULL) {
curr = head = temp;
} else {
curr->next = temp;
curr = temp;
}
This makes curr always point to the last node, and head always points to the beginning of the list.
Am I wrong or do you set head->next = temp; but never set head to a valid object before?
You either have to initialize head with an 'empty' Student at the beginning or you just have to set head = temp;
Assuming that you've defined the head variable outside the createListNode() function, here's the problem:
Memory is never assigned to it, since you have never called malloc()

Resources