I opened a thread yesterday asking about how I should proceed. Here is what I've got so far (sorry for the language, the program will be in Albanian but I'm sure you are getting the point).
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
void regjistrim();
void kerkim();
void modifikim();
void fshirje();
void rradhitje();
void display();
void load();
#define EMRI 50
#define MBIEMRI 50
#define ID 20
#define TEL 20
#define EMAIL 25
typedef struct node
{
char emri[EMRI];
char mbiemri[MBIEMRI];
char id[ID];
char tel[TEL];
char email[EMAIL];
struct node* next;
} node;
FILE* addressbook;
node* mynode;
node* curr;
int main(void)
{
char input[2];
int choice;
load();
printf("----------------ADDRESS BOOK----------------");
printf("\n\n\t1 - Regjistrimi i ri\n");
printf("\n\t2 - Kerkim\n");
printf("\n\t3 - Modifikim\n");
printf("\n\t4 - Fshirje\n");
printf("\n\t5 - Rradhitje\n");
printf("\n\t6 - Afishim i address book\n");
printf("\n\t0 - Exit\n");
fgets(input, 4, stdin);
sscanf(input, "%d", &choice);
while (choice < 0 || choice > 6)
{
printf("\nShtypni nje numer nga 0 - 6: \n");
fgets(input, 4, stdin);
sscanf(input, "%d", &choice);
}
switch (choice)
{
case 1:
regjistrim();
break;
case 2:
kerkim();
break;
case 3:
modifikim();
break;
case 4:
fshirje();
break;
case 5:
rradhitje();
break;
case 6:
display();
break;
case 0:
exit(0);
break;
}
return 0;
}
void load()
{
addressbook = fopen("Addressbook.txt", "r");
if (addressbook == NULL)
printf("Could not open file.");
node* mynode = malloc(sizeof(node));
while (fscanf(addressbook, "%s %s %s %s %s", mynode->emri, mynode->mbiemri, mynode->id, mynode->tel, mynode->email) != EOF)
{
node* mynode = malloc(sizeof(node));
mynode = mynode->next;
}
mynode = curr;
}
//Regjistron nje qytetar ne addressbook
void regjistrim()
{
char input[2];
char answer;
do
{
node* newnode = malloc(sizeof(node));
curr->next = newnode;
addressbook = fopen("Addressbook.txt", "a+");
printf("\nShtypni emrin: ");
fgets(newnode->emri, EMRI, stdin);
printf("\nShtypni mbiemrin: ");
fgets(newnode->mbiemri, MBIEMRI, stdin);
printf("\nShtypni ID-in: ");
fgets(newnode->id, ID, stdin);
printf("\nShtypni nr. telefoni: ");
fgets(newnode->tel, TEL, stdin);
printf("\nShtypni email-in: ");
fgets(newnode->email, EMAIL, stdin);
fprintf(addressbook, "Emri: %sMbiemri: %sID: %sNr. telefoni: %sEmail: %s\n", newnode->emri, newnode->mbiemri, newnode->id, newnode->tel, newnode->email);
fclose(addressbook);
printf("\nShtypni y/Y neqoftese doni te regjistroni person tjeter: ");
fgets(input, 50, stdin);
sscanf(input, "%c", &answer);
curr = newnode;
}
while(answer == 'y' || answer == 'Y');
}
void kerkim()
{
//TODO
}
void modifikim()
{
//TODO
}
void fshirje()
{
//TODO
}
void rradhitje()
{
//TODO
}
void display()
{
//TODO
}
Currently I'm getting a segfault at this point: while (fscanf(addressbook, "%s %s %s %s %s", mynode->emri, mynode->mbiemri, mynode->id, mynode->tel, mynode->email) != EOF)
I am mainly concerned about the load() function which will load any entries to the linked list, if there are any, before the user does any action.
Thank you in advance.
I recommend you check out existing implementations of linked lists. Add helper functions for inserting nodes, appending nodes, deleting nodes, and those sorts of things. Once you have a working implementation for the lists themselves, you will have an easier time writing code that takes advantage of the linked list data structure.
Consider changing your node structure to include a pointer to the actual data and a pointer to the next node (instead of actually having the data in the struct itself). Better practice this way. You could even expand your linked list implementation to support any data type by using void pointers as "generic" pointers.
A segmentation fault with linked lists usually indicates that you are dereferencing a NULL pointer somewhere in your code (but is not the case all of the time!). Ensure that you are not referencing nodes that have been deleted or nodes that have not been allocated yet.
If you are satisfied with your current implementation, step through the code with gdb and set a breakpoint where the program is segfaulting. You should not have a hard time tracking down where the bug is.
If you want to create a linked list try doing this
Replace this code
node* mynode = malloc(sizeof(node));
while (fscanf(addressbook, "%s %s %s %s %s", mynode->emri, mynode->mbiemri, mynode->id, mynode->tel, mynode->email) == 5)
{
node* mynode = malloc(sizeof(node));
mynode = mynode->next;
}
mynode = curr;
with this
node* mynodeLocal = malloc(sizeof(node));
node *secondLastNode;
while (fscanf(addressbook, "%s %s %s %s %s", mynodeLocal->emri, mynodeLocal->mbiemri, mynodeLocal->id, mynodeLocal->tel, mynodeLocal->email) != EOF)
{
secondLastNode = mynodeLocal;
mynodeLocal->next = malloc(sizeof(node));
mynodeLocal = mynodeLocal->next;
mynodeLocal->next = NULL;
}
secondeLastNode->next = NULL;
mynode = curr;//Change this line according to your requirement.
Related
I'm studying linked list and try to implement some basic function.
My code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_LEN_NAME 5
#define INSERT_NODE 1
#define APPEND_NODE 2
#define DEL_HEAD 3
#define DEL_TAIL 4
#define SHOW_LIST 5
struct people {
int id;
char name[MAX_LEN_NAME];
struct people *next;
};
typedef struct people people_list;
static void node_insert(people_list** head_ref, int id, const char* name);
static void node_append(people_list** head_ref, int id, const char* name);
static void node_del_head(people_list** head_ref);
static void node_del_tail(people_list** head_ref);
static void node_show(people_list** head_ref);
static void node_insert(people_list** head_ref, int id, const char* name)
{
people_list* new_node = NULL;
new_node = malloc(sizeof(people_list));
memset(new_node, 0, sizeof(people_list));
if (new_node == NULL) {
printf("Fail to allocate memory for new node\n");
exit(1);
}
new_node->id = id;
memcpy(new_node->name, name, sizeof(name));
new_node->next = *head_ref;
*head_ref = new_node;
}
static void node_append(people_list** head_ref, int id, const char* name)
{
return;
}
static void node_del_head(people_list** head_ref)
{
return;
}
static void node_del_tail(people_list** head_ref)
{
return;
}
static void node_show(people_list** head_ref)
{
people_list* current = NULL;
current = *head_ref;
if (current == NULL) {
printf("Empty list, nothing to show\n");
return;
}
printf("Elements in the list: \n");
while (current != NULL) {
printf("id = %d, name = %s\n", current->id, current->name);
current = current->next;
if (current == NULL) {
printf("This is the last element\n");
}
}
}
int main(void)
{
people_list* list = NULL;
#if 0
people_list* second = NULL;
people_list* third = NULL;
list = malloc(sizeof(people_list));
memset(list, 0, sizeof(people_list));
second = malloc(sizeof(people_list));
memset(second, 0, sizeof(people_list));
third = malloc(sizeof(people_list));
memset(third, 0, sizeof(people_list));
list->id = 1;
memcpy(list->name, "duc", sizeof("duc"));
list->next = second;
second->id = 2;
memcpy(second->name, "hy", sizeof("hy"));
second->next = third;
third->id = 3;
memcpy(third->name, "bo", sizeof("bo"));
third->next = NULL;
#endif
char id;
char name[MAX_LEN_NAME] = {0};
int option = 0;
while (1) {
printf("******************\n");
printf("1: Insert new node\n");
printf("2: Append new node\n");
printf("3: Delete head node\n");
printf("4: Delete tail node\n");
printf("5: Show list\n");
printf("Insert your choice: ");
scanf("%d", &option);
switch (option) {
case INSERT_NODE:
printf("debug: list=%08Xh\n", list);
printf("Enter id's value: ");
scanf("%d", &id);
printf("Enter name: ");
scanf("%s", name);
printf("debug: list=%08Xh\n", list);
node_insert(&list, id, name);
memset(name, 0, MAX_LEN_NAME);
break;
case APPEND_NODE:
printf("Enter id's value: ");
scanf("%d", &id);
printf("Enter name: ");
scanf("%s", name);
node_insert(&list, id, name);
memset(name, 0, MAX_LEN_NAME);
break;
case DEL_HEAD:
node_del_head(&list);
break;
case DEL_TAIL:
node_del_tail(&list);
break;
case SHOW_LIST:
node_show(&list);
break;
default:
printf ("Invalid input, closing program...\n");
exit(0);
break;
}
}
}
How I test:
Insert first node (1, jon)
Insert second node (2, may)
Show list => Only second node is printed.
I found that, the second->next does not point to the first node, it points to a NULL node. I keep debugging as below
case INSERT_NODE:
printf("debug: list=%08Xh\n", list);
printf("Enter id's value: ");
scanf("%d", &id);
printf("Enter name: ");
scanf("%s", name);
printf("debug: list=%08Xh\n", list);
node_insert(&list, id, name);
memset(name, 0, MAX_LEN_NAME);
break;
and the console log is
Insert your choice: 1
debug: list=001F29A8h => This is the address holding value of the first node
Enter id's value: 2
Enter name: may
debug: list=00000000h => The value of list is clear ???
As you can see, the value of the ponter list changes without modifying. Please help me to fix this. Thank you.
As pointed out in the comments, with scanf("%d", &id);, %d is expecting an int *. Change
char id;
char name[MAX_LEN_NAME] = {0};
to
int id;
char name[MAX_LEN_NAME] = {0};
Otherwise you invoke Undefined Behaviour by passing an unexpected type to scanf.
With good compiler warnings, we get the following:
warning: 'memcpy' call operates on objects of type 'const char'
while the size is based on a different type 'const char *'
[-Wsizeof-pointer-memaccess]
memcpy(new_node->name, name, sizeof(name));
When the sizeof operator is used on a pointer (in this case, a const char *) the result is the size of the pointer in bytes, not the object it points to.
Change this function call to
memcpy(new_node->name, name, sizeof new_node->name);
/* OR memcpy(new_node->name, name, MAX_LEN_NAME); */
or more appropriately, use strcpy
strcpy(new_node->name, name);
Suggest changing #define MAX_LEN_NAME 5 to something more reasonable like #define MAX_LEN_NAME 128.
At the same time, scanf("%s", name); should be changed to have a field width specifier that limits the length of the input read, to avoid buffer overflows. This should be the buffer size minus one (e.g., scanf("%127s", name);).
Additionally, the return value of scanf should not be ignored. Ensure it is the expected number of successful conversions, and otherwise handle failure. E.g.,
if (2 != scanf("%d%u", &si, &us)) {
fprintf(stderr, "Failed to read inputs.\n");
exit(EXIT_FAILURE);
}
Aside: These function calls
people_list* new_node = NULL;
new_node = malloc(sizeof(people_list));
memset(new_node, 0, sizeof(people_list));
can be simplified with calloc
people_list *new_node = calloc(1, sizeof *new_node);
I've written a linked list program and want to take input with spaces but it's not working.It works fine when I simply use "scanf" with %s but since I want to take input with multiple spaces I tried using "gets" and "puts" I've also tried using scanf("%[^\n]*c"); but on the console it gives me random garbage value for scanf("%[^\n]*c"); and for "gets" it reads blank space,
now let me tell you guys some info about the code and how it works
createNode(); function basically just creates a new node to store in the list and returns the address of this newly created node to the insertend(); function where it aligns the new node at the end of the list and in start=t=newnode start is the head pointer which points to the very first node and t is used to traverse the list until t's value becomes NULL,As you could see in the else part of the insertend(); function we're using another pointer t and storing the value of start in it so that we can traverse the list without losing the the address of the first node which is originally kept in the start pointer.
here's the code ->
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct Node
{
char first[20];
struct Node* next;
};
struct Node* start=NULL;
struct Node* t,*u;
int i=1;
struct Node* createNode() //this function creates a newnode everytime it's called
{
struct Node* create=(struct Node*)malloc(sizeof(struct Node));
return create;
}
int length() //to measure the length of the list.
{
int count = 0;
struct Node* temp;
temp=start;
while(temp!=NULL)
{
count++;
temp = temp->next;
}
return count;
}
void insertend() //to insert a node at the end of the list.
{
int l;
struct Node* newnode = createNode();
printf("Enter Name : ");
fgets(newnode->first,sizeof(newnode->first),stdin);
if(start==NULL)
{
start=t=newnode;
start->next=NULL;
}
else
{
t=start;
while(t->next!=NULL)
t=t->next;
t->next=newnode;
t=newnode;
t->next=NULL;
printf("%s successfully added to the list!",newnode->first);
}
l=length();
printf("The length of the list is %d",l);
}
void display() //to display the list
{
struct Node* dis;
dis=start;
if(start==NULL)
{
system("cls");
printf("No elements to display in the list");
}
else
{
system("cls");
for(int j=1;dis!=NULL;j++)
{
printf("%d.) %s\n",j,dis->first);
dis=dis->next;
}
}
}
int menu() //this is just a menu it returns the user input to the main function
{
int men;
printf("Please select a choice from the options below :-\n\n");
printf("1.) Add at the end of the list\n");
printf("2.) Display list\n");
printf("3.) exit\n");
printf(" Enter your choice : ");
scanf("%d",&men);
return men;
}
int main()
{
while(1)
{
system("cls");
switch(menu())
{
case 1 : insertend();
break;
case 2 : display();
break;
case 3: exit(0);
default : system("cls"); printf("Ivalid choice!Please select an appropriate option!");
fflush(stdin);
break;
}
getch();
}
return 0;
}
gets is not to be used, it has been removed from C standard due to it's lack of security.
If you want to know more read Why is the gets function so dangerous that it should not be used?
If you use [^\n] it should work, though it's also problematic since this specifier does not limit the lenght of the stream to be read only that it must stop when finding a newline character.
I suspect the problem might be in the container rather than in the reading, maybe uninitialized memory, If you provide the struct code it'll be easier to diagnose.
You can try:
fgets(newnode->first, sizeof(newnode->first), stdin)
There is a caveat:
If the inputed stream is larger than the container, the extra characters will remain in the input buffer, you might need to discard them.
EDIT:
So the main problem was the fact that through your code you have lingering characters in the buffer, in the particular case of your fgets input it would catch a '\n' left in the buffer, so it would read it before the inputed stream, leaving it, again, in the buffer.
I added a function to clean up buffer, note that fflush(stdin) leads to undefined behaviour so it's a bad option.
I also added a few small tweaks.
- Note that conio.h is windows specific as is system("cls") and getch()(ncurses.h in Linux systems) so I commented it for this sample.
Live sample here
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
struct Node
{
char first[20];
struct Node *next;
};
struct Node *start = NULL;
struct Node *t, *u;
void clear_buf(){ //clear stdin buffer
int c;
while((c = fgetc(stdin)) != '\n' && c != EOF){}
}
struct Node *createNode() //this function creates a newnode everytime it's called
{
struct Node *create = malloc(sizeof(struct Node));
return create;
}
int length() //to measure the length of the list.
{
int count = 0;
struct Node *temp;
temp = start;
while (temp != NULL)
{
count++;
temp = temp->next;
}
return count;
}
void insertend() //to insert a node at the end of the list.
{
int l;
struct Node *newnode = createNode();
printf("Enter Name : ");
clear_buf(); //clear buffer before input
fgets(newnode->first, sizeof(newnode->first), stdin);
newnode->first[strcspn(newnode->first, "\n")] = '\0'; //remove '\n' from char array
if (start == NULL)
{
start = t = newnode;
start->next = NULL;
printf("%s successfully added to the list!", newnode->first);
}
else
{
t = start;
while (t->next != NULL)
t = t->next;
t->next = newnode;
t = newnode;
t->next = NULL;
printf("%s successfully added to the list!", newnode->first);
}
l = length();
printf("The length of the list is %d", l);
}
void display() //to display the list
{
const struct Node *dis;
dis = start;
if (start == NULL)
{
system("cls");
printf("No elements to display in the list");
}
else
{
system("cls");
for (int j = 1; dis != NULL; j++)
{
printf("%d.) %s\n", j, dis->first);
dis = dis->next;
}
}
}
int menu() //this is just a menu it returns the user input to the main function
{
int men;
printf("\nPlease select a choice from the options below :-\n\n");
printf("1.) Add at the end of the list\n");
printf("2.) Display list\n");
printf("3.) exit\n");
printf(" Enter your choice : ");
scanf("%d", &men);
return men;
}
int main()
{
while (1)
{
system("cls");
switch (menu())
{
case 1:
insertend();
break;
case 2:
display();
break;
case 3:
exit(0);
default:
system("cls");
printf("Ivalid choice!Please select an appropriate option!");
clear_buf();
break;
}
getch();
}
return 0;
}
Hello I am new to c so I had a few issues with my code. My code is supposed to display a menu which displays if you want to add, search, delete, or print all. This works however, my insertion part doesn't. When I select add and start typing the information I want the program crashes?
here is my code
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#pragma warning(disable: 4996)
//#define max 100
typedef enum { diploma, bachelor, master, doctor } education;
struct person { // a node to hold personal details
char name[30];
char email[30];
int phone;
education degree;
struct person* next;
} *head;
void branching(char c);
int insertion();
struct person *search(char *sname);
void deletion(char *sname);
void print_all();
char *x;
//Main Method
int main() { // print a menu for selection
char ch;
do {
printf("Enter your selection\n");
printf("\ti: insert a new entry\n");
printf("\td: delete an entry\n");
printf("\ts: search an entry\n");
printf("\tp: print all entries\n");
printf("\tq: quit \n");
ch = tolower(getchar());
branching(ch);
} while (ch != 113);
return 0;
}
void branching(char c) { // branch to different tasks
switch (c) {
case 'i':
insertion();
break;
case 's':
printf("Enter an item to search");
scanf("%s", x);
search(x);
break;
case 'd':
printf("Enter an item to delete");
scanf("%s", x);
deletion(x);
break;
case 'p':
print_all();
break;
case 'q':
break;
default:
printf("Invalid input\n");
}
}
//insert entry
int insertion(){
struct person *p;
p = (struct person*)malloc(sizeof(struct person));
if (p == 0){
printf("There are no more places to insert.\n"); return -1;
}
printf("Enter name, email, phone, and degree:\n");
scanf("%s", p->name);
scanf("%d", &p->phone);
scanf("%s", p->email);
scanf("%i", p->degree);
p->next = head;
head = p;
return 0;
}
//search method
struct person *search(char *sname){
struct person *p = head, *b = p;
printf("Please enter the name you wish to search:\n");
scanf("%c", sname);
while (p != 0)
if (strcmp(sname, p->name) == 0){
printf("Phone: %d\n", p->phone);
printf("Email: %s\n", p->email);
printf("Degree: %s\n", p->degree);
return b;
}
else{
b = p;
p = p->next;
}
printf("The name does not exist.\n");
return 0;
}
//delete entry
void deletion(char *sname){
struct person *t, *p;
p = head;
t = head;
while (t != NULL){
if (t->name == sname){
if (t == head){//case 1
head = t->next;
free(t);
return;
}
else{
p->next = t->next;
free(t);
return;
}
}
else{
p = t;
t = t->next;
}
}
return;
}
//print
void print_all(){
struct person *p;
p = head;
if (p = NULL){
printf("No entries found.");
}
else{
while (p != NULL){
printf("%s", p->name);
printf("%d", p->phone);
printf("%s", p->email);
printf("%s", p->degree);
p = p->next;
}
printf("\n");
}
}
The variable x needs to point to valid memory. When you make the declaration:
char * x;
The pointer is uninitialized and could point to anywhere in the computer's memory range.
This is why we recommend using std::string and the C++ streams, such as:
std::string x;
cin >> x;
// or
std::getline(cin, x);
Remember, if you dynamically allocate memory for C-Style strings, you have to deallocate the memory.
Also, you need to specify a maximum string length for your input. This is why scanf is an evil function. If you must use scanf, prefer it's other family members, such as fscanf(stdin) or use a format specifier with the maximum size specified.
When comparing the C-style string you will need to use strcmp. When copying the string, use strcpy. If you use std::string, you could use assignment operator and relational operators (more convenient and safe).
I'm reading in from a text file likewise:
George Washington, 2345678
John Adams, 3456789
Thomas Jefferson, 4567890
James Madison, 0987654
James Monroe, 9876543
John Quincy Adams, 8765432
Andrew Jackson, 7654321
Martin Van Buren, 6543210
William Henry Harrison, 5432109
John Tyler, 4321098
The function to delete the name works, however when it is successful the printf statements just continue to loop in the command window. I tried using a break statement at the end of the loop, however that only led to saying that the name wasn't found. Can someone offer any insight?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Creates node for holding student's information
struct node
{
char name [50];
int id;
struct node *next;
}*head;
//Create Function Prototypes
void readDataFile ();
void insert(char *inName, char *inID);
void display(struct node *d);
int deleteID(int num);
void deleteName(char *delete_name);
//Main function
int main()
{
//Declare variables
int i, num, delete_id, id;
char *name;
char nameDelete [50];
char nameInsert [50];
struct node *n;
//initialize link list
head = NULL;
//Read in file
readDataFile();
//Create list of operations utilized in program
while (1)
{
printf("\nList Operations\n");
printf("===============\n");
printf("1.Insert\n");
printf("2.Display\n");
printf("3.Delete by ID\n");
printf("4.Delete by Name\n");
printf("5.Exit\n");
printf("Enter your choice : ");
if(scanf("%d", &i) <= 0)
{
printf("Enter only an Integer\n");
exit(0);
}
else
{
switch(i)
{
case 1:
getchar();
printf("Enter the name to insert:");
scanf("%[^\n]s", nameInsert);
printf("\nEnter the ID associated with the name: ");
scanf("%d", &id);
break;
case 2:
if (head == NULL)
printf("List is Empty\n");
else
{
printf("Elements in the list are:\n");
}
display(n);
break;
case 3:
if(head == NULL)
printf("List is Empty\n");
else
{
printf("Enter the ID number to delete: ");
scanf("%d", &delete_id);
}
if(deleteID(delete_id))
printf("%d deleted successfully \n", delete_id);
else
printf("%d not found in the list\n", delete_id);
break;
case 4:
getchar();
if(head == NULL)
printf("List is Empty\n");
else
{
printf("Enter name to delete: ");
scanf("%[^\n]s", nameDelete);
printf("Checking for name %s...\n", nameDelete);
printf("%s not found in the list\n", nameDelete);
deleteName(nameDelete);
}
break;
case 5:
return 0;
default:
printf("Invalid option\n");
}
}
}
return 0;
}
//Define the functions
//Function to delete by name
void deleteName(char *delete_name)
{
//Create temporary and helper node
struct node *temp, *helper;
//Set temp equal to head
temp = head;
//Loop until the end of the list
while(temp != NULL)
{
if(strcmp(temp->name, delete_name) == 0)
{
if(temp == head)
{
head = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
}
else
{
helper->next = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
}
}
else
{
helper = temp;
temp = temp->next;
}
}
break;
}
Please learn how to make an MCVE (How to create a Minimal, Complete, and Verifiable Example?) or SSCCE (Short, Self-Contained, Correct Example) — two names and links for the same basic idea.
Here's an MCVE derived from your code. I added the missing break; or return; from the loop in deleteName(). I rewrote main() essentially completely, but it works cleanly:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
char name[50];
int id;
struct node *next;
} *head;
void deleteName(char *delete_name);
int main(void)
{
struct node *n;
head = NULL;
head = malloc(sizeof(*head));
assert(head != 0);
strcpy(head->name, "Abraham Lincoln");
head->id = 1;
head->next = 0;
n = malloc(sizeof(*n));
strcpy(n->name, "George Washington");
n->id = 2;
n->next = head;
head = n;
n = malloc(sizeof(*n));
strcpy(n->name, "John Adams");
n->id = 3;
n->next = head;
head = n;
deleteName("George Washington");
deleteName("John Adams");
deleteName("Abraham Lincoln");
return 0;
}
void deleteName(char *delete_name)
{
struct node *temp, *helper = 0;
temp = head;
while (temp != NULL)
{
if (strcmp(temp->name, delete_name) == 0)
{
if (temp == head)
{
head = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
}
else
{
helper->next = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
}
return; // The key change!
}
else
{
helper = temp;
temp = temp->next;
}
}
}
This ran cleanly under valgrind and Mac OS X 10.10.2 with GCC 4.9.1.
Found George Washington!
George Washington deleted successfully
Found John Adams!
John Adams deleted successfully
Found Abraham Lincoln!
Abraham Lincoln deleted successfully
It is important to learn how to be brutal about stripping out irrelevant code when creating an MCVE.
The break keyword will break out of the nearest switch or loop. Therefore, you can try this:
while(temp != NULL)
{
if(strcmp(temp->name, delete_name) == 0)
{
if(temp == head)
{
head = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
break;
}
else
{
helper->next = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
break;
}
}
else
{
helper = temp;
temp = temp->next;
}
}
An alternate solution is to set temp to NULL after you free it (this is good practice anyway, so that might be another idea)
Add the return statement at the end of the if block once you have found the name to be deleted
void deleteName(char *delete_name)
{
//Create temporary and helper node
struct node *temp, *helper;
//Set temp equal to head
temp = head;
//Loop until the end of the list
while(temp != NULL)
{
if(strcmp(temp->name, delete_name) == 0)
{
if(temp == head)
{
head = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
}
else
{
helper->next = temp->next;
free(temp);
printf("Found %s!\n", delete_name);
printf("%s deleted successfully\n", delete_name);
}
return;
}
else
{
helper = temp;
temp = temp->next;
}
}
}
Also you how are planning to indicate that the name isn't part of the list, as opposed to being found and deleted. You can change modify the function such that it returns an error code when it hasn't found the name in the list, which is then used to indicate the name was never in the list to begin with.
I did not really look at the delete() function,
however, the following code illustrates how the
code should be formatted, etc.
notice the checking for input errors, which should always be performed
notice the separation of the struct definition from the struct declaration
notice the easy readability of the switch cases
by incorporating some vertical white space
notice the simple comments after the closing braces
notice that no call to an action is performed if the linked list is empty
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//define node for holding student's information
struct node
{
char name [50];
int id;
struct node *next;
};
// create head pointer for linked list of student info
// and initialize
struct node *head = NULL;
// Function Prototypes
// note: if all these functions are only accessed within this file
// then they should be declared with the 'static' modifier
void readDataFile (void);
void insert (char *inName, char *inID);
void display (struct node *d);
void deleteID (int delete_ID);
void deleteName (char *delete_name);
//Main function
int main()
{
//Declare local variables
// notice use of meaningful names
// notice, for readability and documentation,
// only one variable declared per line
int i; // receives user menu selection input
int idDelete;
int idInsert;
char nameDelete [50];
char nameInsert [50];
// struct node *n; // unused variable
int done = 0; // used to exit when user enters '5'
//Read in file
readDataFile();
//Create list of operations utilized in program
while (!done)
{
printf("\nList Operations\n");
printf("===============\n");
printf("1.Insert\n");
printf("2.Display\n");
printf("3.Delete by ID\n");
printf("4.Delete by Name\n");
printf("5.Exit\n");
printf("Enter your choice : ");
if( 1 != scanf("%d", &i) )
{ // then, scanf failed
perror( "scanf for choice failed");
exit( EXIT_FAILURE );
}
// implied else, scanf successful
switch(i)
{
case 1:
int ch;
while(EOF != (ch = getchar()) && (ch != '\n'));
printf("Enter the name to insert:");
if( 1 != (scanf("%[^\n]s", nameInsert) ) )
{ // then scanf failed
perror( "scanf for new student name failed");
exit( EXIT_FAILURE );
}
// implied else, scanf successful
printf("\nEnter the ID associated with the name: ");
if( 1 != (scanf("%d", &idInsert) ) )
{ // then scanf failed
perror( "scanf for new student ID failed");
exit( EXIT_FAILURE );
}
// implied else, scanf successful
insert( nameInsert, idInsert );
break;
case 2:
if (head == NULL)
printf("List is Empty\n");
else
{
printf("Elements in the list are:\n");
display(n);
} // end if
break;
case 3:
if(head == NULL)
printf("List is Empty\n");
else
{
printf("Enter the ID number to delete: ");
if( 1 != (scanf("%d", &idDelete) ) )
{ // then, scanf failed
perror( "scanf for ID to delete failed");
exit( EXIT_FAILURE );
}
// implied else, scanf successful
deleteID(idDelete);
} // end if
break;
case 4:
int ch;
while(EOF != (ch = getchar()) && (ch != '\n'));
if(head == NULL)
printf("List is Empty\n");
else
{
printf("Enter name to delete: ");
if( 1 != (scanf("%[^\n]s", nameDelete) ) )
{ // then, scanf failed
perror( "scanf for name to delete failed");
exit( EXIT_FAILURE );
}
// implied else, scanf successful
printf("Checking for name %s...\n", nameDelete);
deleteName(nameDelete);
} // end if
break;
case 5:
done = 1; // this will cause while() loop to exit
break;
default:
printf("Invalid option\n");
break;
} // end switch
} // end while
return 0;
} // end of function: main
The problem was solved. A guy gave it in comments. The problem was that I was using %d to read in a short int. I should have used %hd or I should have used an `int'.
I tried to create a program of singly-linked list using only local variables. I was able to make a working program by using global variables.
The program with local variables compiles but it crashes when I try to traverse the linked list.
I have absolutely no idea what is wrong with the implementation with local variables. What is the problem present in the Implementation with local variables?
ABOUT THE STRUCTURE OF THE PROGRAMS:
I understand that the programs are big so I'll put in something about structure of the program.
The program is structured as a menu driven program. So the initial calls to functions are in main() function
There are 3 options in main() menu - exit, traverse and insertion
Exit returns 0 to exit program while other 2 do function calls
Insertion function itself is arranged as menu-driven program.
It has 3 options - return , insert_begin and insert_end. The last 2 are function calls.
I know there are memory leaks as I haven't freed any memory but I will take care of that after I can understand the problem in the current program.
//WORKING IMPLEMENTATION USING GLOBAL VARIABLE
#include<stdio.h>
#include<stdlib.h>
#define MIN 0
#define MAX 2
#define INS_MIN 0
#define INS_MAX 2
typedef struct node
{
int data;
struct node *next;
}sll_node;
sll_node *start = NULL;
void intro()
{
system("cls");
printf("\n\tThese are the various options:\n");
printf("\n\t00 Exit");
printf("\n\t01 Traverse the list");
printf("\n\t02 Insertion into the list");
}
void insert_begin()
{
sll_node *node = malloc(sizeof(sll_node));
if(node == NULL)
{
printf("\n\tNot enough menory");
exit(-1);
}
int data;
printf("\n\tData to be entered: ");
scanf("%d", &data);
node->data = data;
node-> next = start;
start = node;
}
void insert_end()
{
sll_node *node = malloc(sizeof(sll_node));
if(node == NULL)
{
printf("\n\tNot enough menory");
exit(-2);
}
if(start == NULL)
insert_begin();
else
{
printf("\n\tData to be entered: ");
scanf("%d", &(node->data));
node-> next = NULL;
sll_node *node2;
for(node2 = start; node2->next != NULL; node2 = node2->next)
;
node2->next = node;
}
}
void insert_intro()
{
system("cls");
printf("\n\tThese are the various options:\n");
printf("\n\t00 Insertion Done");
printf("\n\t01 Insert at beginning");
printf("\n\t02 Insert at end");
}
void insertion()
{
short choice;
while(1)
{
choice = -1;
while(choice < INS_MIN || choice > INS_MAX)
{
insert_intro();
printf("\n\n\tEnter your chocie: ");
scanf("%d", &choice);
}
switch(choice)
{
case 0:
return;
case 1:
insert_begin();
break;
case 2:
insert_end();
break;
}
}
}
void traverse()
{
if(start == NULL)
printf("\n\n\tLinked list is empty");
else
{
printf("\n\n\t");
for(sll_node *node = start; node != NULL; node = node->next)
printf("%d ", node->data);
}
getch();
}
int main()
{
short choice;
while(1)
{
choice = -1;
while(choice < MIN || choice > MAX)
{
intro();
printf("\n\n\tEnter your choice: ");
scanf("%d", &choice);
}
switch(choice)
{
case 0:
return 0;
case 1:
traverse();
break;
case 2:
insertion();
break;
}
}
return 0;
}
//COMPILES BUT CRASHES - Same program but with local variable start and variable passing between functions
#include<stdio.h>
#include<stdlib.h>
#define MIN 0
#define MAX 2
#define INS_MIN 0
#define INS_MAX 2
typedef struct node
{
int data;
struct node *next;
}sll_node;
void intro()
{
system("cls");
printf("\n\tThese are the various options:\n");
printf("\n\t00 Exit");
printf("\n\t01 Traverse the list");
printf("\n\t02 Insertion into the list");
}
sll_node* insert_begin(sll_node *start)
{
sll_node *node = malloc(sizeof(sll_node));
if(node == NULL)
{
printf("\n\tNot enough menory");
exit(-1);
}
int data;
printf("\n\tData to be entered: ");
scanf("%d", &data);
node->data = data;
node-> next = start;
return node;
}
sll_node* insert_end(sll_node *start)
{
sll_node *node = malloc(sizeof(sll_node));
if(node == NULL)
{
printf("\n\tNot enough menory");
exit(-2);
}
if(start == NULL)
start = insert_begin(start);
else
{
printf("\n\tData to be entered: ");
scanf("%d", &(node->data));
node-> next = NULL;
sll_node *node2;
for(node2 = start; node2->next != NULL; node2 = node2->next)
;
node2->next = node;
}
return start;
}
void insert_intro()
{
system("cls");
printf("\n\tThese are the various options:\n");
printf("\n\t00 Insertion Done");
printf("\n\t01 Insert at beginning");
printf("\n\t02 Insert at end");
}
sll_node* insertion(sll_node *start)
{
short choice;
while(1)
{
choice = -1;
while(choice < INS_MIN || choice > INS_MAX)
{
insert_intro();
printf("\n\n\tEnter your chocie: ");
scanf("%d", &choice);
}
switch(choice)
{
case 0:
return start;
case 1:
start = insert_begin(start);
break;
case 2:
start = insert_end(start);
break;
}
}
}
void traverse(sll_node *start)
{
if(start == NULL)
printf("\n\n\tLinked list is empty");
else
{
printf("\n\n\t");
for(sll_node *node = start; node != NULL; node = node->next)
printf("%d ", node->data);
}
getch();
}
int main()
{
sll_node *start = NULL;
short choice;
while(1)
{
choice = -1;
while(choice < MIN || choice > MAX)
{
intro();
printf("\n\n\tEnter your choice: ");
scanf("%d", &choice);
}
switch(choice)
{
case 0:
return 0;
case 1:
traverse(start);
break;
case 2:
start = insertion(start);
break;
}
}
return 0;
}
You are not returning anything from insertion() function when item is added to a list. So linked list may not get constructed properly.
Probably, you should return start only when its added at the beginning, otherwise start in main() will not point to head of the list.
sll_node* insertion(sll_node *start)
{
...
switch(choice)
{
case 0:
return start;
case 1:
start = insert_begin(start);
return start; //<----- return node
break;
case 2:
start = insert_end(start);
break;
}
...
}
Change short choice to int choice.
Why does this make a difference?
Short answer is that printf("%d") expects an integer.
The long answer is "%d" describes the data type you are passing to printf as an integer (which is commonly 4 to 8 bytes), and you're giving it a datatype of short - which is commonly 2 bytes long. When your program reads the input and stores it at the pointer, &choice, it writes 4 bytes starting at that address (but only 2 were reserved). This causes a segmentation fault and will crash your program.
Here's a list to some printf documentation. You'll notice that to pass a short to printf you would write %hd instead of %d
When i compile your code on my computer, it works, but i changed "short choice" to "int choice", because scanf("%d", &choice) takes 4 bytes to write on, and when choice is short it crashes, because short has only 2 bytes, therefore stack corruption will occur, my be on your computer this corruption damage the "start" pointer.
About the crash. Change the argument start in both functions insert_begin and insert_end to sll_node ** start, and when assigning new value, use the expression *start = your-new-value. It is because you have to pass a pointer to the local variable start which is also pointer. You do not need to change function traverse.
About memory leaks, let me to point-out that when you call insert_begin from inside insert_end, the node created from insert_end is left unused. before exit() and the return in main() you should free the list.
Yes, sorry. There was another bug hard to see. It was at 2 lines where you read (choice).
short choice;
...
// It is ERROR to use "%d" with (short choice), because the stack will
// be overwritten with unsuspected results. The format specifier "%hd"
// say to compiler that (&choice) point to a short 16-bit integer,
// not 32-bit
scanf("%hd", &choice);
This is slightly different version, tested, without memory leaks.
//
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define MIN 0
#define MAX 2
#define INS_MIN 0
#define INS_MAX 2
typedef struct node
{
int data;
struct node *next;
} sll_node;
void clear_list(sll_node** start)
{
assert(start != NULL);
sll_node* node = *start;
while (node != NULL)
{
sll_node* element = node;
node = element->next;
free(element);
}
*start = NULL;
}
void intro()
{
system("cls");
printf("\n\tThese are the various options:\n");
printf("\n\t00 Exit");
printf("\n\t01 Traverse the list");
printf("\n\t02 Insertion into the list");
}
void insert_begin(sll_node** pstart)
{
sll_node* node = (sll_node*)malloc(sizeof(sll_node));
if (node == NULL)
{
printf("\n\tNot enough menory");
clear_list(pstart);
exit(-1);
}
int data;
printf("\n\tData to be entered: ");
scanf_s("%d", &data);//scanf
node->data = data;
node->next = *pstart;
// update the local variable start passed from main to point just inserted node
*pstart = node;
}
void insert_end(sll_node** start)
{
assert(start != NULL);
if (*start == NULL)
{
insert_begin(start);
}
else
{
sll_node* node = (sll_node*)malloc(sizeof(sll_node));
if (node == NULL)
{
printf("\n\tNot enough menory");
clear_list(start);
exit(-2);
}
printf("\n\tData to be entered: ");
scanf("%d", &(node->data));
node->next = NULL;
sll_node* node2;
for(node2 = *start; node2->next != NULL; node2 = node2->next)
;
node2->next = node;
}
}
void insert_intro()
{
system("cls");
printf("\n\tThese are the various options:\n");
printf("\n\t00 Insertion Done");
printf("\n\t01 Insert at beginning");
printf("\n\t02 Insert at end");
}
void insertion(sll_node** start)
{
short choice;
while(1)
{
choice = -1;
while(choice < INS_MIN || choice > INS_MAX)
{
insert_intro();
printf("\n\n\tEnter your chocie: ");
scanf("%hd", &choice);
}
switch(choice)
{
case 0:
return;
case 1:
insert_begin(start);
break;
case 2:
insert_end(start);
break;
}
}
}
void traverse(sll_node *start)
{
if (start == NULL)
printf("\n\n\tLinked list is empty");
else
{
printf("\n\n\t");
for(sll_node *node = start; node != NULL; node = node->next)
printf("%d ", node->data);
}
getch();
}
int main()
{
sll_node *start = NULL;
short choice;
while(1)
{
choice = -1;
while(choice < MIN || choice > MAX)
{
intro();
printf("\n\n\tEnter your choice: ");
scanf("%hd", &choice);
}
switch(choice)
{
case 0:
clear_list(&start);
return 0;
case 1:
traverse(start);
break;
case 2:
insertion(&start);
break;
}
}
return 0;
}
P.S. Very hard to edit! I'm new here and do not have enough experience. Wasted a lot of time to edit!