Why is this C linked list program giving 'segmentation fault'? - c

The first function reads a file that has a bunch of 'char's and puts them in a linked list. It is not working :(.
#include <stdio.h>
#include <stdlib.h>
struct list {
char val;
struct list* next;
};
typedef struct list element;
int lcreate(char* fname, element* list);
int ldelete(element* list);
int linsert(char a, char b, element* list);
int lremove(char a, element* list);
int lsave(char* fname, element* list);
int lcreate(char* fname, element* list) {
element* elem = list;
char c = 0;
FILE * file = NULL;
file = fopen(fname, "r");
while ((c = getc(file)) != EOF)
{
if(list == NULL) {
list = (element*)malloc(sizeof(element));
if(list == NULL) {
return 0;
}
list->val = c;
}
else {
elem->next=(element*)malloc(sizeof(element));
elem = elem->next;
elem-> val = c;
}
}
fclose(file);
elem->next = NULL;
return 1;
}
int main(void) {
int i = 0;
element * list = NULL;
lcreate("list.txt", list);
for(i = 0; i<4; ++i) {
printf("%c", list->val);
list = list->next;
}
return 0;
}
Fixed problem with 'file' being null.

One obvious problem is right here:
FILE * file = NULL;
fopen(fname, "r");
For the fopen to accomplish much, you need to assign the result from fopen to your FILE *:
file = fopen(fname, "r");
Edit: Since you're working in C, you can't pass the pointer by reference. As an alternative, you can pass a pointer to a pointer:
int lcreate(char *fname, element **list) {
// ...
*list = malloc(sizeof(element));
(*list)->next = null;
(*list)->val = c;
// ...
}
Basically, all the code inside of lcreate will need to refer to *list instead of just list. Alternatively, you can take a pointer to an existing list as input, and return a pointer to the list, so in main you'd have something like: list = lcreate("list.txt", list);

file is NULL, and you never assign a file handle to it.

In your main function, you are also passing list by value to lcreate. Within the lcreate() function, you are overwriting a local copy of list, not changing the value of list in the main function. Since list is initialized to NULL, you will get a segfault when you call list->val.

Yep -- what the others said about the FILE pointer, and passing list by value rather than reference to lcreate(), is true.
You also aren't returning the size of the list from lcreate() -- you should probably return this via the return value or a pointer argument.
You are attempting to iterate through the list 4 times in the main() function, but there may be less than 4 items in the list. Eventually the printf() will cause a segmentation fault if list is NULL.
If you still have issues after making these changes, I would recommend adding tracing to your code to work out at which point the segmentation fault is happening.
Update:
Also please remember to free the memory you have allocated after you traverse the list, otherwise you'll end up with a memory leak (although in practice this won't really be an issue for you as the program is ending, but freeing memory is a good habit to get into).

I can see an additional problem as well. In the while statement of lcreate() the true clause of the if statement malloc's some memory and assigns it to list however elem is not updated.
while ((c = getc(file)) != EOF)
{
if(list == NULL) {
list = (element*)malloc(sizeof(element));
if(list == NULL) {
return 0;
}
list->val = c;
}
else {
Next time through the while loop list will not be non-null but elem is still null so the assignment of elem->next tries to deference the null pointer and thus the segmentation fault (which, btw, means that you tried to access memory that has not been assigned to your process):-
else {
elem->next=(element*)malloc(sizeof(element));
As others have pointed out you also don't return list back to main so it will still be NULL when you hit the printf() loop.
Finally, the debugger is your friend when looking at these problems. You'll see exactly which line triggers the seg fault and what the state of the variables were.

It would be good to check if the malloc was successful by checking for a non null pinter.
Also, you might want to allocate the head/first link outside of the while to avoid the null check for the head every time in the while loop. Of course, these are optimizations, in case your linked list grows really large!

Related

Segmentation Fault in C function desired to edit a node in a Linked List, as well as Alphabetizing the List itself

I have been tasked with writing a function that must edit a node in a linked list. I have the following code so far that seems to be somewhat working, but there are still some problems:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct flavor {
char *name;
int available;
struct flavor *next;
};
// "Head" node
struct flavor *first = NULL;
// Current node
struct flavor *current = NULL;
void insert(char *name, int available) {
struct flavor *node = (struct flavor*)malloc(sizeof(struct flavor));
node->name = name;
node->available = available;
node->next = NULL;
if (first == NULL) {
first = node;
return;
}
current = first;
while (current->next != NULL) {
current = current->next;
}
current->next = node;
}
void sort(struct flavor *first) {
char *i;
struct flavor *temp1;
struct flavor *temp2;
for (temp1 = first; temp1 != NULL; temp1 = temp1->next) {
for (temp2 = temp1->next; temp2 != NULL; temp2 = temp2->next) {
if (temp2->name < temp1->name) {
i = temp1->name;
temp1->name = temp2->name;
temp2->name = i;
}
}
}
}
void display() {
struct flavor *ptr = first;
printf("Available shake flavors\n");
while (ptr != NULL) {
printf("\nName: %s\n", ptr->name);
printf("Available: %d\n", ptr->available);
ptr = ptr->next;
}
}
struct flavor *edit_flavor(struct flavor *first, char *old_name, char *new_name) {
if (first == NULL) {
printf("List is empty");
return;
}
current = first;
while (current->next != NULL) {
if (current->name == old_name) {
current->name = new_name;
printf("\nFlavor %s replaced with %lu\n", old_name, new_name);
return;
}
current = current->next;
}
printf("\n%s does not exist in the list\n", old_name);
}
int main(void) {
char *old = "";
char *new = "";
insert("Banana", 1);
insert("Mango", 0);
insert("Strawberry", 1);
insert("Apple", 1);
sort(first);
display();
printf("\nPlease enter old flavor: ");
scanf("%s", &old);
printf("Please enter new name: ");
scanf("%s", &new);
edit_flavor(first, old, new);
sort(first);
display();
return 0;
}
Whenever I try to use the program at all, I keep getting the error segmentation fault (core dumped) and I am not sure what I have done wrong?
On top of that, my insert into the "Linked List" seems to be working just fine, but I need the list to be in alphabetical order and it also needs to be updated in alphabetical order once a node has been edited. I have attempted to implement the sort() function, but it is not working at all and I think it may be due to the fact that I'm not comparing the names correctly (as they are strings?). How can I fix the sort function in order to correctly alphabetize the list?
For starters, do not use global variables such as first and current.
The function interface is inconsistent. For example the function edit_flavo, on the one hand, accepts the head node first through its first parameter. On the other hand, it uses the global variable current.
The function sort is incorrect. It does not swap values of the data member available. This comparison in the if statement
if (temp2->name < temp1->name) {
does not make sense because there are compared addresses of string literals instead of comparing the pointed string literals themselves using the C string function strcmp.
The list should not keeps addresses of string literals but it should make copies of passed strings in a dynamically allocated memory.
And it will be more correct to swap entire nodes in the sort function instead of swapping data members of pointed nodes.
In function edit_flavor this if statement
if (current->name == old_name) {
in general also is incorrect. Whether equal string literals are stored by the compiler as separate character arrays or as a one array depends on the a compiler options. So it is not necessary that for example the condition in such an if statement
if ( "Hello" == "Hello" )
will yield true. Again you need to use the C standard string function strcmp to compare pointed string literals.
In Main you declared two pointers to string literals.
char *old = "";
char *new = "";
You may not change string literals. Any attempt to change a string literal results in undefined behavior. You need to declare character arrays where the strings entered by the user will be stored. So one more the list shall store deep copes of strings not their addresses.
These statements
scanf("%s", &old);
scanf("%s", &new);
are incorrect. Firstly the argument expressions shall have the type char * as the type of variables old and new instead of the type char ** that the expressions &old and &new have. And secondly such a call invokes undefined behavior because the function scanf will try to change the string literals pointed to by the pointers.
So you need to redesign your list. Start from removing the global variable first and the pointer to the head node make a local variable in main initializing it by NULL. Then change the function insert by allocating dynamically memory for a copy of passed to the function string. Rewrite the function sort that will swap entire nodes instead of separate data members.
And for entered strings use character arrays.

C: Adding data from text file into a singly linked list

I'm recently started learning C programming. I have some java experience so I know my way around codes, I like to think..
This little thing I'm working on is killing me.
I'm trying to make a program that read lines from a text file -> store it in a singly linked list -> print out the singly linked list
This is my code so far:
typedef struct node {
char *data;
struct node *next;
} node;
node *start = NULL;
node *current;
void add(char *line) {
node *temp = malloc(sizeof(node));
// This line under I believe where my problem is...
temp->data = line;
temp->next = NULL;
current = start;
if(start == NULL) {
start = temp;
} else {
while(current->next != NULL) {
current = current->next;
}
current->next = temp;
}
}
This is my function for reading the file and sending characters to the add function
void readfile(char *filename) {
FILE *file = fopen(filename, "r");
if(file == NULL) {
exit(1);
}
char buffer[512];
while(fgets(buffer, sizeof(buffer), file) != NULL) {
// I've tried to just send i.e: "abc" to the add function
// which makes the program work.
// like: add("abc");
// my display method prints out abc, but when I'm sending buffer
// it prints out nothing
// Thing is, I've spent way to much time trying to figure out what
// I'm doing wrong here...
add(buffer);
}
fclose(file);
}
I'm sure this is a fairly simple problem, but I've spent way too much time with this problem.
And if there is anything other that look off/could be better I appreciate feeback on that aswell :)
Try:
temp->data = strdup(line);
to duplicate (make a copy of) what line points at.
Otherwise every line points at the buffer which is getting overwritten with each new line.
You need to allocate memory for the string - each line is read into buf and so you need to copy it out or it will be overwritten with subsequent lines. I suggest one of two approaches, the first being the simplest with what you already have, but the second being better as you only need to do one free() for each object.
The first one is just a single change to your add() function:
temp->data = malloc(strlen(line)+1);
strcpy(temp->data, line);
Now, when you want to free an object in your linked list you must first call free() on data and then free() on the object itself.
However you can change the structure slightly and then you can allocate the whole object in one go:
typedef struct node {
struct node *next;
char data[0];
} node;
Then your add() function would look like this:
void add(char *line) {
node *temp = malloc(sizeof(node)+strlen(line)+1);
strcpy(temp->data, line);
temp->next = NULL;
current = start;
if(start == NULL) {
start = temp;
} else {
while(current->next != NULL) {
current = current->next;
}
current->next = temp;
}
}
Note that of course you should do error checking after each malloc() in production code. When you are done with an object a single free() is enough to free the whole structure.
Edit: The "array length 0" feature is a GCC specific extension, as noted by #crashmstr in the comments. If you use an array length of 1, it should work in any compiler though:
typedef struct node {
struct node *next;
char data[1];
} node;
Since an extra byte is already allocated in this case, the malloc() call in the add() function would then become:
node *temp = malloc(sizeof(node)+strlen(line)+1-1);
(of course the +1-1 can be omitted, but it is just to show that we still need space for the null terminator but an extra byte is already included in the sizeof).

C function returning structure data through one of the arguments

I have an issue when using pointer parameters in a function to return values. The function correctly loads all values inside the function, but then somehow fails to pass the pointer value to the variable in the arguments.
In my case, i wrote a function witch returns 1 or 0 depending on whether allocation of memory in question failed or not, and as one of the parameters, takes a pointer to a list that needs to be entered. The structure of the list looks like this:
typedef struct sList {
int id;
char first_name[30];
char last_name[30];
struct sList *next;
} tList;
The function looks like this:
int readList(tList *start, int n){
tList *head = NULL;
tList *tail = NULL;
int i;
for (i = 0; i < n; i++){
tList *tmp = malloc(sizeof(tList));
if (tmp == NULL) return 0;
scanf("%d %s %s", &tmp->id, &tmp->first_name, &tmp->last_name);
tmp->next = NULL;
if (!head) head = tmp;
else tail->next = tmp;
tail = tmp;
}
start = head;
return 1;
}
And the main method:
void main(){
tList *start = NULL;
int n;
scanf("%d", &n);
readList(start, n);
tList *tmp = start;
while (tmp){
printf("%d %s %s\n", tmp->id, tmp->first_name, tmp->last_name);
tmp = tmp->next;
}
system("PAUSE");
return;
}
During debugging, i have concluded that the list head and start inside the function have all the entered values, but as soon as I leave the function and return to the main program the start list goes bananas. So, my question is, am I doing something wrong, because, to my knowledge, this should work in theory. Thanks in advance.
If you want to change a variable from within a function, you need to pass a pointer to it and dereference that pointer within said function. That's how C emulates pass-by-reference.
When that variable is itself a pointer, that means you need to pass a pointer to the pointer, such as with:
int readList(tList **pStart, int n){
// blah blah blah, setting up head.
*pStart = head;
return 1;
}
int main(void){
tList *start = NULL;
int n;
scanf("%d", &n);
readList(&start, n); // Note this, using address-of
// more blah
return 0;
}
The text below is an aside to your specific problem but I thought I'd mention it for completeness.
Your main function doesn't conform to the canonical ones allowed by the standard - I've changed it to make that more acceptable but it may not be necessary for your particular implementation, depending on how lax it is. It's still a good idea to follow the standard.
It's also dangerous to assume (in robust code) that scanf() always works. If it returns zero (number of items successfully scanned), n will almost certainly not be what you expect.
You make the same mistake with readList() in that you don't check its return value either. It also has the annoying aspect of causing memory leaks if an allocation fails.

Why is this segmentation fault inconsistent across builds?

I wrote a c program, compiled it and it ran fine. After a few compiles - it started giving me a segmentation fault. I renamed the folder, recompiled and it worked again.
Is this something normal? To have an inconsistent segmentation fault? I change the output name, change folder names etc.. and it bounces from giving segmentation fault to not giving seg fault. I don't know what to do anymore.
I mean, if it is a coding problem, seg fault should be consistent, right? I should get it every time. here's the code:
file my_set.c:
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
/*
The program acceps a set of numbers from stdin until EOF
And then prints them (not storing duplicate numbers)
*/
int main ()
{
int num;
nodePtr head; /*head of the list*/
while (scanf("%d", &num) != EOF)
{
addToList(num, &head);
}
printList(head);
freeList(head);
return 0;
}
file list.c:
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
/*
Implements a linked list, each element of which contains a dynamic array.
I used a linked list to maximize potential memory in case it is fragmented.
I use a dynamic array in each node to minimize the percentage of overhead
from creating a list (the pointer, the index...);
*/
/*
Adds number n to list *h
4 cases:
1. list is empty:
creating one
updating h with new list
creating a new dynamic array in the list
updating it and the index
2. can reallocate current node's array for 1 more int
3. cannot reallocate current node's array:
creating a new node
initializing it
4. cannot create a new node
printing the current list, an "out of memory error" and freeing all memory.
*/
void addToList(int n, nodePtr *h)
{
static nodePtr p; /*points to current last node*/
int *temp; /*for use in reallocation*/
if (!*h) /*first item of set*/
{
*h = malloc (sizeof(node));
(*h)->arr = malloc(sizeof(int));
(*h)->arr[0] = n;
(*h)->i = 1;
p = *h;
return;
}
/*if n is already in the list, no need to add it
the call comes after first item, because first item cannot be in the list*/
if(existsInList(n, *h)) return;
/*using realloc while still possible*/
if ((temp = realloc(p->arr, (p->i+1)*sizeof(int))))
{
p->arr = temp;
p->arr[p->i] = n;
p->i++;
return;
}
/*if realloc no longet possible - start new node*/
if ((p->next = malloc(sizeof(node))))
{
p = p->next;
p->arr = malloc(sizeof(int));
p->arr[0] = n;
p->i = 1;
return;
}
/*can no longer start new nodes - quit with error, after printing*/
printf("out of memory!");
printList(*h);
freeList(*h);
}
/*checks if n is in p assuming p is not null
it can asume so because the call for it comes after the check for first item*/
int existsInList(int n, nodePtr p)
{
int i;
for (; p ; p = p->next)
for (i = 0; i < p->i; i++)
if (p->arr[i] == n)
return 1;
return 0;
}
/*frees the list*/
void freeList(nodePtr p)
{
nodePtr temp = p;
if (!p) return; /*list is empty*/
while (p)
{
free(p->arr);
p = p->next;
free(temp);
}
}
/*prints the content of the list to stdout*/
void printList(nodePtr p)
{
if (!p) return;
int i;
printf("\n");
for (; p ; p = p->next)
for (i = 0; i < p->i; i++)
printf("%d ", p->arr[i]);
printf("\n");
}
file list.h:
/*
pointer to a node
declare a variable of this type to create a list
then start adding to the list
*/
typedef struct s *nodePtr;
/*the struct that represents each node of the list
reason for dynamic array is in "list.c"
*/
typedef struct s
{
int *arr;
int i; /*index for next num, also size of array;*/
nodePtr next;
}node;
/*Adds the int to list at nodePtr omitting duplicates*/
void addToList(int, nodePtr*);
/*prints a list*/
void printList(nodePtr);
/*returns 1 if an int exists in list referenced by nodePtr, 0 otherwise*/
int existsInList(int, nodePtr);
/*frees all dynamically allocated memory*/
void freeList(nodePtr);
Basically all I do is get numbers from stdin, put them in a list(no duplicates) and then print them. I use a list of dynamic arrays.
Initialize your variables!
int num = 0;
nodePtr head = NULL; /*head of the list*/
ADD: The inconsistent behaviour can come from debug vs release compilation, usually compilers in debug mode set the non-initialized variables to weird values like 0xDDDDDDDD to make the problem immediately visible. In release mode if the memory block is zeroed it will happen that the content of the variables is 0 but there is no guarantee for it.
You should check the returns values from malloc() in case it's returning NULL (out of memory).
Intermittent segfaults in c/c++ programs are usually caused by uninitialised memory, often in pointer variables.
You've posted a lot of code, which makes it hard to debug just be reading it. I suggest going through the code and, wherever a variable is declared, giving it an initial value (e.g. zero or NULL). Remember that the compiler will not initialise them for you.
You should probably start by initialising the values of num and head in main(). E.g.
int num = 0;
nodePtr head = NULL; /*head of the list*/
EDIT 1
Another bug is in addToList(). If the first if block in that function is not executed then the value of the local variable p will be uninitailised when you later call realloc(p->arr, ...). When you dereference p to get p->arr, ifp` is uninitialised then you will usually get a segfault.
EDIT 2
Two useful techniques when programming in C/C++ :
Always initialise variables at the point that you declare them. If you don't then their value is undefined. Note that this doesn't solve all problems. If you dereference an uninitialised pointer then you will usually get a segfault. If you initailise it to null and then dereference it then you will always get a segfault. Easier to debug, but it still crashes.
Always declare variables as near as possible to the point in the code that you first use them. This has the effect of reducing the chances of using an uninitialised variable because the compiler will generate an 'undeclared variable' error. The practice of declaring all variables at the start of a function is a hangover from old-style 'K&R' C, where you had to do that. Modern C doesn't require it.
So, instead of:
int foo() // Warning: bad code
{
int a;
int b;
func1();
a=func2(&b);
return a;
}
try something like:
int foo()
{
func1();
int b = 42;
int a = func2(&b);
return a;
}

Creating a singly linked list in C

I'm trying to create a singly linked list from an input text file for an assignment. I'm trying to do it a little bit at a time so I know my code is not complete. I tried creating the head pointer and just printing out its value and I can't even get that to work, but I'm not sure why. I included the struct, my create list, and print list functions. I didn't include the open file since that part works.
typedef struct List
{
struct List *next; /* pointer to the next list node */
char *str; /* pointer to the string represented */
int count; /* # of occurrences of this string */
} LIST;
LIST *CreateList(FILE *fp)
{
char input[LINE_LEN];
LIST *root; /* contains root of list */
size_t strSize;
LIST *newList; /* used to allocate new list members */
while (fscanf(fp, BUFFMT"s", input) != EOF) {
strSize = strlen(input) + 1;
/* create root node if no current root node */
if (root == NULL) {
if ((newList = (LIST *)malloc(sizeof(LIST))) == NULL) {
printf("Out of memory...");
exit(EXIT_FAILURE);
}
if ((char *)malloc(sizeof(strSize)) == NULL) {
printf("Not enough memory for %s", input);
exit(EXIT_FAILURE);
}
memcpy(newList->str, input, strSize); /*copy string */
newList->count = START_COUNT;
newList->next = NULL;
root = newList;
}
}
return root;
}
/* Prints sinly linked list and returns head pointer */
LIST *PrintList(const LIST *head)
{
int count;
for (count = 1; head != NULL; head = head->next, head++) {
printf("%s %d", head->str, head->count);
}
return head; /* does this actually return the start of head ptr, b/c I want to
return the start of the head ptr. */
}
root has an undefined value, so it won't initialize. The second line of CreateList should be
LIST *root = NULL;
Also, further down there is allocation apparently for the details of the item, but a) the code fails to capture the allocation and save it anywhere, and b) the size of the allocation should be strSize, not the length of the variable itself. There are several ways to fix it, but the most straightforward would be:
newList->str = (char *)malloc(strSize);
if (newList->str == NULL)
The second malloc allocates memory but its return value is not assigned to anything, so that allocated memory is lost.
newList is allocated but not initialized, so using a memcpy to copy memory to newList->str will fail since newList->str points to nothing. Probably you wanted the result of the second malloc to be assigned to newList->str, but you forgot it.
You shouldn't be incrementing head after head = head->next in the for loop. PrintList will return NULL every time since the loop wont stop until head is NULL. Why do you need to return the head of the list you just passed to the function anyway?
Edit:
LIST *current = head;
while (current != NULL) {
printf("%s %d", current->str, current->count);
current = current->next;
}

Resources