I'm learning C data structures. I've the following code, which is resulting in a segmentation fault.
I've used the hash function from K&R C (2nd Ed.).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASHSIZE 101
typedef struct Details
{
char *name;
float checkTotal;
float tipTotal;
int numReciepts;
struct Details *next;
struct Details *prev;
} det;
static det *hashtab[HASHSIZE];
char *str_dup(char *);
det *lookup(char *);
det *insert(char *, float, float);
unsigned hash(char *);
void printData(char *);
int main()
{
printf("1. Enter details.\n");
printf("2. Print List of employees.\n");
char *s;
float cT; //checkTotal
float tT; //tipTotal
while (1)
{
int command = 0;
printf("Enter option: ");
scanf("%d", &command);
if (command == 1)
{
printf("Enter name: ");
scanf("%s", s);
printf("Enter check total: ");
scanf("%f", &cT);
printf("Enter tip total: ");
scanf("%f", &tT);
insert(s, cT, tT);
}
else if (command == 2)
{
char *p;
printf("Enter name of employee: ");
scanf("%s", p); //including this line causes seg fault.
printData(p);
}
else
break;
}
return 0;
}
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; *s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
det *lookup(char *name)
{
det *ptr;
for (ptr = hashtab[hash(name)]; ptr != NULL; ptr = ptr->next)
{
if (strcmp(name, ptr->name) == 0)
return ptr;
}
return NULL;
}
det *insert(char *name, float cT, float tT)
{
det *new;
unsigned hashval;
if ((new = lookup(name)) == NULL)
{
new = (det *)malloc(sizeof(new));
if (new == NULL || (new->name = str_dup(name)) == NULL)
return NULL;
hashval = hash(name);
new->next = hashtab[hashval];
hashtab[hashval] = new;
new->checkTotal += cT;
new->tipTotal += tT;
new->numReciepts++;
printf("Details of '%s' inserted.\n", name);
}
else
{
new->checkTotal += cT;
new->tipTotal += tT;
new->numReciepts++;
printf("Details of '%s' updated.\n", name);
}
}
char *str_dup(char *s)
{
char *p;
p = (char *)malloc(strlen(s) + 1);
if (p != NULL)
strcpy(p, s);
return p;
}
void printData(char *p)
{
det *ptr;
if ((ptr = lookup(p)) != NULL)
{
printf("Emplyee Name : %s\n", ptr->name);
printf("Total amount : %6.2f\n", ptr->checkTotal);
printf("Total tip : %6.2f\n", ptr->tipTotal);
printf("Total reciepts: %6.2d\n", ptr->numReciepts);
}
else
{
printf("Employee '%s' does not exist.\n", p);
return;
}
}
Without the inclusion of scanf() in else if:main, every thing works fine. Calling printData() with a hard coded string also works.
Inclusion of scanf() in else if:main results in segmentation fault after prompting for name.
I have tried and can not think of the reason. As far as I know:
my code in if:main is not accessing anything in else if:main.
if should not break because of something wrong in else if even if it is wrong. (at least in this scenario, or as far as my understanding goes)
Thanks and any tips for future from experts are appreciated!
P.S: scanf():if is working perfectly.
Related
The program currently only asks the user to input courses, I want the list of courses the user entered to be stored in struct Students{ char courses[NUM_COURSES][100];. The list of courses the user entered should be able to be printed by using
printf("Courses %s\n",temp->courses);
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#define NUM_COURSES 5
int i = 0;
struct Students
{
char first_name[100];
char last_name[100];
int age;
char address[100];
char programme[100];
char courses[NUM_COURSES][100];
int num_selected_courses;
struct Students *next;
} * head;
void enter_student(char *first_name, char *last_name, int age, char *address, char *programme, char *courses, int num_selected_courses)
{
struct Students *student = (struct Students *)malloc(sizeof(struct Students));
strcpy(student->first_name, first_name);
strcpy(student->last_name, last_name);
student->age = age;
strcpy(student->address, address);
strcpy(student->programme, programme);
strcpy(student->courses[i], courses);
student->num_selected_courses = num_selected_courses;
student->next = NULL;
if (head == NULL)
{
// if head is NULL
// set student as the new head
head = student;
}
else
{
// if list is not empty
// insert student in beginning of head
student->next = head;
head = student;
}
}
void search_fname(char *first_name, char *last_name, int age, char *address, char *programme, char *courses, int num_selected_courses)
{
struct Students *temp = head;
char a[50];
int j;
printf("Enter First Name of Student to Search:\n");
scanf("%s", &a);
while (temp != NULL)
{
for (int j = 1; j <= i; j++)
{
if (!strcmp(first_name, a))
{
printf("The Students Details are:\n");
printf("First Name: %s\n", temp->first_name);
printf("Last Name: %s\n", temp->last_name);
printf("Age:%d\n", temp->age);
printf("Address: %s\n", temp->address);
printf("Programme: %s\n", temp->programme);
printf("Courses %s\n", temp->courses); /// list of courses for student name entered prints here
return;
}
}
temp = temp->next;
}
printf("Student name %s not found!!!\n\n\n", a);
}
int main()
{
struct Students stud;
head = NULL;
char first_name[100];
char last_name[100];
int age;
char address[100];
char programme[100];
int selected_courses[NUM_COURSES];
int num_selected_courses = 0;
int c;
const char *const list_courses[NUM_COURSES] = {
"CSE1100",
"CSE1101",
"CSE1102",
"ITE1100",
"ITE1101"};
do
{
int input_valid = 0;
int selected_course;
int d;
while (!input_valid)
{
char input[100];
// prompt user for input
printf(
"Courses available:\n"
" CSE1100\n"
" CSE1101\n"
" CSE1102\n"
" ITE1100\n"
" ITE1101\n"
"\n"
"Enter Course: ");
// attempt to read one word of user input
if (scanf("%99s", input) != 1)
{
fprintf(stderr, "unexpected input failure!\n");
exit(EXIT_FAILURE);
}
// discard remainder of input line
do
{
d = getchar();
} while (d != '\n' && d != EOF);
// determine whether course entered was valid or not
for (int i = 0; i < NUM_COURSES; i++)
{
if (strcmp(input, list_courses[i]) == 0)
{
input_valid = 1;
selected_course = i;
break;
}
}
// if invalid, print error message
if (!input_valid)
printf("Invalid choice! Try again.\n\n");
}
// input was valid, so add course
selected_courses[num_selected_courses] = selected_course;
num_selected_courses++;
// if we have already reached the maximum number of
// courses, then don't ask again
// ask user whether he wants to add another course
printf("Would you like to enter another course? (y or n)\n");
c = d = getchar();
// add spacing
printf("\n");
// discard remainder of input line
while (d != '\n' && d != EOF)
d = getchar();
} while (c == 'y');
for (int i = 0; i < num_selected_courses; i++)
{
// COPYING A SELECTED COURSE INTO STUDENT STRUCT
strcpy(stud.courses[i], list_courses[selected_courses[i]]);
}
// POPULATE NUMBER OF SELECTED COURSES IN STUDENT STRUCT
stud.num_selected_courses = num_selected_courses;
printf("You have selected the following courses:\n");
// PRINTING ALL COURSES IN THE STUDENT STRUCT
for (int i = 0; i < stud.num_selected_courses; i++)
// PRINTING A COURSE IN THE STUDENT STRUCT
printf("%s\n", stud.courses[i]);
}
If you want to be able to use the line
printf("Courses %s\n",temp->courses);
in order to print the courses member of struct Students, then you must make the courses member a null-terminated string. However, you are currently declaring that member as an array of strings.
Therefore, in order to do what you say you want, you would have to change the declaration
char courses[NUM_COURSES][100];
in struct Students to something like:
char courses[100];
You probably want the content of the string to be something like:
"CSE1100, CSE1102, ITE1101"
I have shown you in my answer to your previous question how to build such a string.
I am trying to sort the following structure. I am using the qsort to order the books according to date publish in order of the newest first. I completely don't understand why the pointer can't access the date-published element.
#include <string.h>
#include <stdlib.h>
#include "problem5.h"
int int_cmp(const void *a, const void *b)
{
//const int *ia = (const int *)a;
//const int *ib = (const int *)b;
//return *ia - *ib;
return (*(int*)a - *(int*)b);
}
int main()
{
struct book* books = NULL; // no books at all initially so we initialize to NULL
// so we can simply use realloc
int numberofbooks = 0;
int programend = 0;
while (programend == 0)
{
printf("1. Add Book\n");
printf("2. View Books\n");
printf("3. Quit\n");
int command;
int i, j;
scanf("%d", &command);
if (command == 1)
{
getchar(); // consume Enter key (due su scanf)
// allocate memory for one more book
books = realloc(books, sizeof(struct book) * (numberofbooks + 1));
printf("Enter Name\n");
gets(books[numberofbooks].name);
printf("Enter Author\n");
gets(books[numberofbooks].author);
printf("Enter Year Published\n");
scanf("%d", &books[numberofbooks].year_published);
numberofbooks++; // increment number of books
printf(books.year_published);
}
else if (command == 2)
{
qsort(books->year_published, numberofbooks, sizeof(int), int_cmp);
for (i = 0; i < numberofbooks; i++)
{
printf("%d - %s by %s\n", books[i].year_published, books[i].name, books[i].author);
}
}
else if (command == 3)
{
programend = 1;
}
//else if and the else will prevent infinite loop when the user enters invalid choice in the beginning.
else if (command != 1 || command != 2 || command != 3)
{
printf("Invalid choice!\n");
}
else {return 0;}
}
free(books);
return 0;
}
I think the problem is the pointer in the qsort() but I don't know how to correct that. I tried using qsort(books, numberofbooks, sizeof(int), int_cmp); but the books weren't ordered as expected.
Here is an example of a multikey sort:
int
cmp_multikey(const void *a,const void *b)
{
const struct book *booka = a;
const struct book *bookb = b;
int cmp;
do {
// sort by year published
cmp = booka->year_published - bookb->year_published;
if (cmp)
break;
// sort by author
cmp = strcmp(booka->author,bookb->author);
if (cmp)
break;
// sort by title
cmp = strcmp(booka->name,bookb->name);
if (cmp)
break;
} while (0);
return cmp;
}
Invoke with:
qsort(books,numberofbooks,sizeof(struct book),cmp_multikey);
Some other tips ...
[As others have mentioned] Never use gets. Use a switch/case instead of an if/else ladder.
Try to avoid intermixing scanf and fgets.
Personally, I prefer to [always] use fgets. Here is a [safe] replacement for gets and a replacment for scanf("%d",&num);:
int
getstr(const char *prompt,char *buf,int buflen)
{
char *cp;
printf("%s",prompt);
fflush(stdout);
cp = fgets(buf,buflen,stdin);
if (cp == NULL) {
fprintf(stderr,"unexpected EOF\n");
exit(1);
}
// find newline
cp = strchr(buf,'\n');
// ensure we had enough space
if (cp == NULL) {
fprintf(stderr,"response too large for buffer\n");
exit(1);
}
// strip newline
*cp = 0;
}
int
getnum(const char *prompt)
{
char buf[1000];
int num;
getstr(prompt,buf,sizeof(buf));
num = atoi(buf);
return num;
}
This task is to get the first line as a title then calculate data from a file ,the minus number is by parentheses. It will print the title and he sum of these data. each line is terminated by newline character. I don't know what the problem is. I don't know how to deal with "Bus error 10". Maybe because of the allocation of memory, I have no idea... Can anyone help me?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Our definition of a structure to represent department data.
struct dept {
int id;
char *name;
int balance;
struct dept *next;
};
typedef struct dept dept_t;
// For (a)
dept_t *create_dept(int dept_id, char *dept_name, int dept_balance);
// For (b)
dept_t *load_dept(int id);
// For (c)
void free_dept(dept_t *dept);
// For (d)
void print_dept(dept_t *dept);
int main(int argc, char *argv[])
{
dept_t *d = load_dept(51423);
print_dept(d);
free_dept(d);
}
// (a)
dept_t *create_dept(int dept_id, char *dept_name, int dept_balance)
{
dept_t *d = malloc(sizeof(dept_t));
d->id = dept_id;
d->name = dept_name;
d->balance = dept_balance;
d->next = NULL;
return d;
}
// (b)
char *prompt(FILE *fp)
{
char ch;
// skip leading whitespace
do
{
fscanf(fp, "%c", &ch);
if(feof(fp))
{
return NULL;
}
} while(ch == '\n');
// read in until whitespace
int cur_size = 8;
char *str = malloc(sizeof(char) * cur_size);
int cur_pos = 0;
str[cur_pos] = ch;
cur_pos++;
do
{
fscanf(fp, "%c", &ch);
if(feof(fp))
{
str[cur_pos] = '\0';
return str;
}
str[cur_pos] = ch;
cur_pos++;
if(cur_pos >= cur_size - 1)
{
cur_size = cur_size * 2;
str = realloc(str, sizeof(char) * cur_size);
}
} while(ch != '\n');
str[cur_pos - 1] = '\0';
return str;
}
dept_t *load_dept(int id)
{
FILE *fp;
char *filename;
int balance = 0;
char *name;
char *string;
int i;
dept_t *d;
filename = malloc(sizeof(char)*10);
sprintf(filename,"%d.txt",id);
if((fp = fopen(filename,"r")) == NULL)
{
fprintf (stdout,"Can't open \"%s\"file.\n",filename);
exit(1);
}
name = prompt(fp);
int value;
for(i=0;i<6;i++)
{
string = prompt(fp);
if (string[0]=='(')
{
value = atoi(&string[1]);
balance = balance - value;
}
else
{
value = atoi(string);
balance = balance + value;
}
}
free(string);
free(filename);
if(fclose(fp)!=0)
{
fprintf(stderr,"Error closing file\n");
}
d = create_dept(id,name,balance);
return d;
}
// For (c)
void free_dept(dept_t *dept)
{
free(dept->name);
free(dept);
}
// For (d)
void print_dept(dept_t *dept)
{
printf("Department: %s",dept->name);
printf(" %d",dept->balance);
}
Since, as user3629249 noted, the function: prompt() can return a null pointer, you should change
for(i=0;i<6;i++)
{
string = prompt(fp);
to
while (string = prompt(fp))
{
(then less than 6 data lines won't cause a fault).
We have a small program where the input comes from a text file (see text sample below) and is used to scan and write certain student information to different struct variables. However, when the add_student() function is called it gives this bizzare output (see screenshot below).
struct student_list sl;
struct teacher_list tl;
struct data {
char *name;
int number;
char index;};
struct student {
struct data *d;
struct student *next;};
struct student_list{
int size;
struct student *front;
struct student *tail;};
struct teacher{
struct data *d;
struct teacher *next;};
struct teacher_list{
int size;
struct teacher *front;
struct teacher *tail;};
void main()
{
readAndLoad();
print_students();
}
void readAndLoad()
{
int c;
int i=0;
char line[200];
int number, semNum;
char name[100];
char index;
while ((c=getchar())!=EOF)
{
if(c != '\n')
{
line[i++] = c;
line[i] = '\0';
/*printf("%c ", c);
printf("%s \n", line);*/
}else
{
//printf("\n");
int j, b;
b = 0;
for (j = 0; j < sizeof(line); j++)
{
if (line[j] == ' ')
++b;
}
//printf("%s \n", line);
if (b == 2)
{
if (line[0] == 'S')
{
sscanf(line, "S %d %s", &number, name);
struct student *studentnode;
studentnode = malloc(sizeof(struct student));
add_student(&studentnode, number, &name);
} else if (line[0] == 'T')
{
sscanf(line, "T %d %s", &number, name);
struct teacher *teachernode;
teachernode = malloc(sizeof(struct teacher));
add_teacher(&teachernode, number, &name);
}
}
memset(&line[0], 0, sizeof(line));
i=0;
}
}
//printf(line);
}
void add_student(struct student *n, int student_number, char *student_name)
{
//---------------------------------------------------
printf("%s\n", student_name);
n->d->name = student_name;
n->d->number = student_number;
n->d->index = 'S';
n->next = 0;
printf("%s\n", n->d->name);
//---------------------------------------------------
if (sl.size == 0)
{
sl.front = n;
sl.tail = n;
printf("%s %d \n", n->d->name, n->d->number);
} else
{
sl.tail->next = n;
sl.tail = n;
printf("%s %d \n", n->d->name, n->d->number);
}
sl.size++;
printf("Student added\n");
}
void add_teacher(struct teacher *n, int number, char *name)
{
n->d->name = name;
n->d->number = number;
n->d->index = 'T';
n->next = 0;
if (tl.size == 0)
{
tl.front = n;
tl.tail = n;
} else
{
tl.tail->next = n;
tl.tail = n;
}
tl.size++;
printf("Teacher added\n");
}
void print_students()
{
int i;
struct student *s = sl.front;
for (i = 0; i < sl.size; i++)
//while (s->next != 0)
{
if (i == (sl.size - 2))
{
printf("%c %s %d", s->d->index, s->d->name, s->d->number);
} else
{
printf("%c %s %d \n", s->d->index, s->d->name, s->d->number);
s = s->next;
}
}
}
Input text file sample
here is the output
Between the highlighted part //-------------------
in the code we can see a correct output of the name from the first printf() but when we go to the second printf() it only prints the blank space... Do you know what could be the problem?
Input text file:
S 123456 Ivan
S 654321 Georgi
T 123456 Jesper
T 123457 Ole
T 123458 Lars
T 123459 Erland
C 31 CALI1 3
C 11 WDDI1 1
C 21 SDJI2 2
E 123456 31
E 123456 11
E 654321 21
A 123456 31
A 123457 11
Console output:
Ivan
123456
Student added
Georgi
,▒( 654321
Student added
Teacher added
Teacher added
Teacher added
Teacher added
E 0▒( 2673448E 0▒( 2673448
studentnode = malloc(sizeof(struct student));
only allocates memory for an instance of student. The memory for studentnode->d has not been allocated. Therefore any n->d->something in add_student() is invalid, thus invokes undefined behavior.
When I gdb the program, it says something is wrong with strcpy, but I do not know why.
Thanks.
The program requires me to read an input file, find the course abbreviation, the course description, the number of credit hours, and the professor name.
Also, I am trying to read the credit hours from the file, which are on the same line as the course. I am trying to only get the credit hours that are on the same line as the course, but it instead prints every number as a credit hour.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 20
typedef struct courses{
char *abbr;
char *name;
int credits;
char *prof;
struct courses *next;
}courses;
int isAbbr(char *string);
int isName(char *string);
int isCredit(char *string);
int isProf(char *string);
courses *readfile(FILE *);
courses *create_course(char *abbr, char *name, int credits, char *prof);
courses *create_list(courses *, courses *);
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("Inadequate amount of arguments.\n");
return 0;
}
FILE *fp = fopen(argv[1], "r");
if (fp == NULL)
{
printf("File cannot be opened.\n");
return 0;
}
courses* head = NULL;
head = readfile(fp);
int choice = 0;
while (choice != 3)
{
printf("\nSelect your option below:\n1-Register for a Course\n2-See my total\n3-Exit\nChoice: ");
scanf("%d",&choice);
}
return 0;
}
courses *readfile(FILE *fp)
{
courses *head, *entry;
head = entry = NULL;
char *abbr = malloc(MAX);
char *namep = malloc(MAX);
namep = "hello";
char *prof = malloc(MAX);
int credit;
int credflag = 0;
int nameFlag = 0;
int profFlag = 0;
int credits = 0;
char line[MAX];
while (fgets(line, MAX - 1, fp) != NULL)
{
if (line[strlen(line) - 1] == '\n')
{
line[strlen(line) - 1] = '\0';
}
char* token = strtok(line," ,\t");
while (token != NULL)
{
if (isAbbr(token) == 1)
{
abbr = token;
credflag = 1;
}
if (isName(token) == 1)
{
credflag = 1;
}
if (isCredit(token) == 1)
{
if(credflag == 1)
{
credits = atoi(token);
credflag = 0;
}
}
if (isProf(token)== 1)
{
if(nameFlag == 1) //print names, reset name flag = 0
{
entry = create_course(abbr, namep, credits, token);
head = create_list(entry,head);
nameFlag = 0;
}
else
{
namep = malloc(sizeof(char));
strcpy(namep, token);
nameFlag = 1;
}
}
else
{
nameFlag = 0;
}
token = strtok(NULL," ,\t");
}
}
}
courses *create_course(char *abbr, char *name, int credits, char *prof)
{
courses *entry = malloc(sizeof(courses));
entry->abbr=(char*)malloc(sizeof(char)*256);
strcpy(entry->abbr, abbr);
entry->name=(char*)malloc(sizeof(char)*256);
strcpy(entry->name, name);
entry->abbr=(char*)malloc(sizeof(char)*256);
strcpy(entry->prof, prof);
entry->credits = credits;
entry->next = NULL;
return entry;
}
courses *create_list(courses *head, courses *entry)
{
if (head == NULL)
{
return entry;
}
courses* curr = head;
while (curr->next != NULL)
{
curr = curr->next;
}
curr->next = entry;
return head;
}
int isProf(char *string)
{
int length = strlen(string);
int i;
if (isupper(string[0]))
{
for (i=1; i<length; i++)
{
if (islower(string[i]))
{
continue;
}
else
{
return 0;
}
}
return 1;
}
}
int isCredit(char *string)
{
int n;
int nfields = sscanf(string, "%d", &n);
if (nfields == 1 && n > 0)
{
return 1;
}
return 0;
}
int isName(char *string)
{
return 1;
}
int isAbbr(char *string)
{
int length = strlen(string);
if (length == 8 && string[4] == '-')
{
printf(" %s\n",string);
return 1;
}
return 1;
}
Just focus on strcpy:
char *namep = malloc(MAX);
namep = "hello";
here you just lose what you malloc for namep, use strcpy or something you want.
namep = malloc(sizeof(char));
strcpy(namep, token);
here you just malloc 1 char for namep, but strcpy auto add NULL terminator, so unless token is "", you overflow namep.
And every strcpy in create_course(), you just malloc 256 and strcpy, what if size of abbr, name, prof > 255? check size or use strncpy().