C segmentation fault 11 when implementing hashtable - c

Doing a relational data structures project where I organize things into a hashtable. So far I've made an insert and lookup method and have no errors. When I try to run the code however I get this:
"Inserting CSG touples"
"segmentation fault 11"
I assume I'm not mallocing something correctly but I can't figure out what and considering it is saying "inserting CSG touples" I don't think its a problem in my createHashTable function. Heres my code
header file: CSG.h
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct CSG{
char* Course;
char* StudentId;
char* Grade;
struct CSG *next;
}CSG;
typedef struct CSGHASH{
int size;
CSG** table;
}CSGHASH;
CSGHASH* createHashTable(int size);
int hash(int CN);
CSG* makeCSG(char* Course, char* StudentId, char* Grade);
void printCSG(CSG guy);
//void printCSGLIST(CSGLIST guy);
int toInt(char* x);
CSG* lookup(CSGHASH *hashtable, char* course, char* StudentId, char* grade);
int insert(CSG* newGuy, CSGHASH* hashtable);
CSG.c
CSGHASH* createHashTable(int size){
CSGHASH* hashtable = NULL;
if(size<1)
return NULL; // table cant be less than length of 1
if((hashtable = malloc(sizeof(CSGHASH*)))== NULL)
return NULL;
if((hashtable->table = malloc(sizeof(CSG*) * size)) == NULL)
return NULL;
for(int i = 0; i<size; i++){
hashtable->table[i] = malloc(sizeof(CSG));
hashtable->table[i] = NULL;
//hashtable->table[i]->next = NULL;
}
hashtable->size = size;
return hashtable;
}
int hash(int CN){
return CN%6;
}
CSG* makeCSG(char* Course, char* StudentId, char* Grade){
//struct CSG tempCSG = malloc(sizeof(CSG));
CSG* tempCSG = malloc(sizeof(CSG*));
strcpy(tempCSG->Course, Course);
strcpy(tempCSG->StudentId, StudentId);
strcpy(tempCSG->Grade, Grade);
return tempCSG;
}
void printCSG(CSG guy){
printf("course: %s\n", guy.Course);
printf("StudentId: %s\n", guy.StudentId);
printf("Grade: %s\n", guy.Grade);
}
// void printCSGLIST(CSGLIST guy){
// }
int toInt(char* x){
int count = 0;
for(int i = 0; i< strlen(x); i++)
count += (int) i;
return count;
}
CSG* lookup(CSGHASH *hashtable, char* course, char* StudentId, char* grade){
CSG* list;
unsigned int hashNum = hash(toInt(course));
for(list = hashtable->table[hashNum]; list!= NULL; list = list->next){
if(strcmp(StudentId, list->StudentId) == 0){
printf("Course: %s\n", list->Course);
printf("Student ID: %s\n", list->StudentId);
printf("Grade: %s\n", list->Grade);
return list;
}
}
printf("doesn't exist\n");
return NULL;
}
int insert(CSG* newGuy, CSGHASH* hashtable){
CSG* list;
CSG* currList;
unsigned int hashNum = hash(toInt(newGuy->Course));
list = malloc(sizeof(CSG));
currList = lookup(hashtable, newGuy-> Course, newGuy-> StudentId, newGuy-> Grade);
if(currList != NULL){
printf("already exists\n");
return 2;
}
list->Grade = strdup(newGuy->Grade);
list->StudentId = strdup(newGuy->StudentId);
list->Course = strdup(newGuy->Course);
list->next = hashtable->table[hashNum];
hashtable->table[hashNum] = list;
printf("CSG inserted\n");
return 0;
}
main file
/*
main4.c
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include"CSG.h"
int main(int argc, char *argv[]){
CSGHASH *testHash = createHashTable(47);
printf("inserting CSG tuples \n");
CSG* tuple1 = makeCSG("CSC101", "12345", "A+");
CSG* tuple2 = makeCSG("CSC101", "67890", "B");
CSG* tuple3 = makeCSG("EE200", "67890", "B+");
CSG* tuple4 = makeCSG("EE200", "45213", "D");
CSG* tuple5 = makeCSG("CSC173", "98765", "C");
CSG* tuple6 = makeCSG("MTH142", "47474", "A");
insert(tuple1, testHash);
lookup(testHash, "CSC101", "12345", "*");
//printCSGLIST(lookup(tester, "CSC101", "12345", "*"));
}
If someone can figure out what I'm doing wrong I'd really appreciate it (sorry I know its a lot to trace through).
UPDATE
after a little debugging it would appear the issue is in the makeCSG function. Hope that makes it a little easier to trace through

A fast pass turned up:
if((hashtable = malloc(sizeof(CSGHASH*)))== NULL)
Err, this will allocate enough storage for a pointer to the CSGHASH and not the CSGHASH itself. Think you want to get rid of the asterisk. The following malloc() will also suffer the same fate.

From CSG.h:
typedef struct CSG{
char* Course;
char* StudentId;
char* Grade;
struct CSG *next;
}CSG;
and in makeCSG() you are doing:
CSG* makeCSG(char* Course, char* StudentId, char* Grade){
//struct CSG tempCSG = malloc(sizeof(CSG));
CSG* tempCSG = malloc(sizeof(CSG*));
strcpy(tempCSG->Course, Course);
strcpy(tempCSG->StudentId, StudentId);
strcpy(tempCSG->Grade, Grade);
return tempCSG;
}
There are couple of problems in makeCSG():
First problem:
CSG* tempCSG = malloc(sizeof(CSG*));
Here, tempCSG is a pointer pointing to CSG. So you should:
CSG* tempCSG = malloc(sizeof(CSG));
Similar issue in createHashTable():
if((hashtable = malloc(sizeof(CSGHASH*)))== NULL)
this should be:
if((hashtable = malloc(sizeof(CSGHASH)))== NULL)
Second problem:
strcpy(tempCSG->Course, Course);
strcpy(tempCSG->StudentId, StudentId);
strcpy(tempCSG->Grade, Grade);
Here, Course, StudentId and Grade are of type char * and you are trying to copy some value to pointers whom you have not allocated memory. Allocate memory before using them.
So first you should do something like this:
CSG* tempCSG = malloc(sizeof(CSG));
tempCSG->Course = malloc(100);
tempCSG->StudentId = malloc(20);
tempCSG->Grade = malloc(10);
and then
strcpy(tempCSG->Course, Course);
strcpy(tempCSG->StudentId, StudentId);
strcpy(tempCSG->Grade, Grade);
tempCSG->next = NULL;
Also, make sure to check the malloc return after every malloc call.

Basically everything envolving char* is wrong. You are not allocating the memory necessary to copy data into those buffers, in fact you are not initializing them at all.
A simplified version of whats wrong with your code:
char* Course;
strcpy(Course, "CSC101");
This is bond to cause fatal errors because we never allocated Course.
You have to either malloc() enough space to fit data in there, for each one of the char* in the structure, or declare them as arrays, so they are already allocated as you reserve memory for the structure they are in, but will have a fixed size determined at compile time.
Example:
typedef struct CSG
{
char Course[16];
char StudentId[16];
char Grade[16];
struct CSG *next;
} CSG;
This really is the easiest way to approach the problem, allows easy store and recover of this structure on a file if you wish to create some sort of basic database, and keeps the code cleaner, avoiding excessive memory management from a ton of malloc() and free() all over the place.

Related

Unwanted pointers modification

Situation: I'm currently working with chained lists and I am adding new elements through a function called inputRegistering(). I am positive that at the end of this function, an element have been added to the correct place.
Issue1: The added element modifies all the other to take its value.
Issue2: When exiting the function, and calling the inputReadingAll() function (which displays the list in its entirety), the elements are now all "empty", or replaced with gibberish (as can do the pointers).
Test1: I have tested to do a manual adding in the main() function and it seems everything worked fine. I can't understand what is the fundamental difference with my code though...
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLEN 256
typedef struct
{
struct elt *first;
struct elt *last;
} Tfile;
typedef struct elt
{
char *val;
struct elt *next;
} Telt;
int fileAdd(Tfile *, char *);
void fileInit(Tfile *);
void inputReadingAll(Tfile *);
void inputRegistering(Tfile *);
int main(int argc, char **argv){
Tfile file;
fileInit(&file);
inputRegistering(&file);
inputRegistering(&file);
inputReadingAll(&file);
}
int fileAdd(Tfile *F, char *newVal){
Telt *newElt;
newElt = (Telt*)malloc(sizeof(Telt));
if(newElt == NULL){
printf("Error\n");
return 0;
}
newElt->val = newVal;
newElt->next = NULL;
if(fileTaille(F) == 0)
F->first = newElt;
else
F->last->next= newElt;
F->last = newElt;
return 1;
}
void fileInit(Tfile *F){
F->first = NULL;
F->last = NULL;
}
void inputReadingAll(Tfile *file){
printf("> Reading all function\nBEGIN\n");
Telt *currElt = file->first;
while(currElt != NULL){
printf("%p, %s\n", currElt, currElt->val);
currElt = currElt->next;
}
printf("END\n");
}
void inputRegistering(Tfile *file){
printf("> Registering function\n> What to register : \n> ");
char temp[MAXLEN];
fgets(temp, MAXLEN, stdin);
temp[strcspn(temp, "\n")] = 0;
printf("Registering %s\n", temp);
fileAdd(file, temp);
}
The (pointer to) local array temp of the function inputRegistering() is passed to the function fileAdd() and the pointer is directly stored to the structure.
This is bad because the array is local and it is invalidated on returning from the function inputRegistering().
Instead of this, the function fileAdd() should copy the passed string and store the pointer to the copy in the structure.
This can be done like this:
newElt->val = malloc(strlen(newVal) + 1); /* +1 for terminating null-character */
if (newElt->val == NULL) {
printf("Error\n");
free(newElt);
return 0;
}
strcpy(newElt->val, newVal);
instead of this:
newElt->val = newVal;

How can I read write this array of structure from/to a file? [duplicate]

I have an array of structs I would like to write to a binary file. I have a write.c program and a read.c program. The write.c program seems to be working properly but when I run the read.c program I get a segmentation fault. I'm new to C so It would be great if someone could look over my code for any obvious errors. I promise it's not too long :)
write.c:
#include <stdlib.h>
#include <stdio.h>
struct Person
{
char f_name[256];
char l_name[256];
int age;
};
int main(int argc, char* argv[])
{
struct Person* people;
int people_count;
printf("How many people would you like to create: ");
scanf("%i", &people_count);
people = malloc(sizeof(struct Person) * people_count);
int n;
for (n = 0; n < people_count; n++)
{
printf("Person %i's First Name: ", n);
scanf("%s", people[n].f_name);
printf("Person %i's Last Name: ", n);
scanf("%s", people[n].l_name);
printf("Person %i's Age: ", n);
scanf("%i", &people[n].age);
}
FILE* data;
if ( (data = fopen("data.bin", "wb")) == NULL )
{
printf("Error opening file\n");
return 1;
}
fwrite(people, sizeof(struct Person) * people_count, 1, data);
fclose(data);
return 0;
}
read.c:
#include <stdlib.h>
#include <stdio.h>
struct Person
{
char f_name[256];
char l_name[256];
int age;
};
int main(int argc, char* argv[])
{
FILE* data;
if ((data = fopen("data.bin", "rb")) == NULL)
{
printf("Error opening file\n");
return 1;
}
struct Person* people;
fread(people, sizeof(struct Person) * 1/* Just read one person */, 1, data);
printf("%s\n", people[0].f_name);
fclose(data);
return 0;
}
Thanks for the help!
struct Person* people;
This allocates just a pointer to struct, but you don't have any allocated space for actual struct contents. Either use malloc similarly to your write program, or try something like:
struct Person people;
fread(&people, sizeof(people), 1, data);
You need to allocate memory for the person first. Change: struct Person* people; into struct Person* people = malloc(sizeof(struct Person));. And don't forget to free your memory at the end: free(people);.
You either need to malloc memory into the pointer variable people before you do the fread, or (easier) just read directly into a local variable:
struct Person people;
fread(&people, sizeof(struct Person) * 1/* Just read one person */, 1, data);
You need to allocate space for the data you are reading in:
people = malloc(sizeof(*people)*numPeople);

Segmentation fault when I try to printf

Why am I getting a segmentationfault when it tries to print the second member in the list?
After printing the first element of the list, the debugger opens the stdio.h and says:
At C:\TDM-GCC-32\include\stdio.h:255
At C:\TDM-GCC-32\include\stdio.h:256
At C:\TDM-GCC-32\include\stdio.h:258
At C:\TDM-GCC-32\include\stdio.h:259
Here is the code.
#include <stdio.h>
#include <stdlib.h>
struct Student {
char *Name;
char *Adresse;
unsigned long Mtnr;
short Kurse;
struct Student *next;
struct Student *previous;
};
typedef struct Student Student;
Student *liste = NULL, *ende = NULL;
void add(char Name, char Adresse, unsigned long Mtnr, short Kurse) {
Student *add;
ende->next = malloc(sizeof(Student));
add = ende->next;
add->Name = Name;
add->Adresse = Adresse;
add->Mtnr = Mtnr;
add->Kurse = Kurse;
add->previous = ende;
add->next = NULL;
ende = ende->next;
}
void Ausgabe(Student *Anfang) {
while (Anfang != NULL) {
printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
Anfang = Anfang->next;
}
}
int main() {
liste = malloc(sizeof(Student));
ende = liste;
liste->Name = "Anna Musterfrau";
liste->Adresse = "Am Schwarzberg-Campus 3";
liste->Mtnr = 22222;
liste->Kurse = 2;
liste->next = NULL;
liste->previous = NULL;
add("Hans Peter", "Kasernenstrasse 4", 4444, 4);
Ausgabe(liste);
return 0;
}
The error is in the declaration of the add() function. The strings should be char pointers, not chars.
void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse){
The signature of the function add is inconsistent to the declaration and usage to the members of Student. Change the signature as follows.
void add(char* Name, char* Adresse, unsigned long Mtnr, short Kurse)
On the long run, it might also be necessary to create copies of Name and Adresse in add, as the caller of add might deallocate them, perhaps causing undesired behaviour.
While Marc Is correct with his observation, there is one more thing you may want to fix here.
When you add a record, you allocate it, but you do not allocate the content of it's pointers (Specifically = for the name and address pointers). The add function just point them to the input's address. this is a problem because the data in the address supplied to the add function is likely to change if, for example, it's a user input, or some other external buffer.
in the code snipped below I fixed the 'name', but left the address as is. please run it and see what happens. (assaf's record displays david's address)
hope this helps
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
char *Name;
char *Adresse;
unsigned long Mtnr;
short Kurse;
struct Student *next;
struct Student *previous;
};
typedef struct Student Student;
Student *liste = NULL, *ende = NULL;
void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse) {
Student *add;
ende->next = malloc(sizeof(Student));
add = ende->next;
add->Name = malloc(strlen(Name)+1);
strcpy(add->Name, Name);
add->Adresse = Adresse;
add->Mtnr = Mtnr;
add->Kurse = Kurse;
add->previous = ende;
add->next = NULL;
ende = ende->next;
}
void Ausgabe(Student *Anfang) {
while (Anfang != NULL) {
printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
Anfang = Anfang->next;
}
}
int main() {
char name_buf[100];
char address_buf[100];
liste = malloc(sizeof(Student));
ende = liste;
liste->Name = "Anna Musterfrau";
liste->Adresse = "Am Schwarzberg-Campus 3";
liste->Mtnr = 22222;
liste->Kurse = 2;
liste->next = NULL;
liste->previous = NULL;
add("Hans Peter", "Kasernenstrasse 4", 4444, 4);
sprintf(name_buf,"assaf stoler");
sprintf(address_buf,"maria 8");
add(name_buf, address_buf, 8888, 8);
sprintf(name_buf,"david david");
sprintf(address_buf,"some street 9");
add(name_buf, address_buf, 9999, 9);
Ausgabe(liste);
return 0;
}
EDIT: Op asked some questions, and the comment space is limited, so I'll add below:
A pointer is just an object pointing somewhere in memory. it's size is fixed. the content it's pointing to will vary.
When you include a pointer to a string in a structure, the space where the string is kept need to be allocated / accounted for. it is not part of the sizeof(struct).
In your original example the pointers were pointing to constant strings (which reside in the static code, usually the data section, allocated by the compiler), which is why your original code was able to access the strings.
In a more realistic case, the input data is not part of the program data, but is received by some input method (which my *buf was to emulate). as such, pointing your name and address at it would break the program, as the pointer you point to may have it's content changed. therefor copying of the data (string / array) is needed, and since we copy the data, we need to allocate space for it, and point our (name/address) pointer at it.
Alternate option is to use non-pointer array for name and address as in:
struct Student {
char Name[20];
char Adresse[60];
unsigned long Mtnr;
...
}
In this scenario, sizeof (struct Student) would actually include all the space for those fields. you still need to use strcpy or memcpy, as well as check for and handle strings that are too long to fit in your pre-defined length.
Hope this helps

Using a string as a variable part of structures's name in a function in plain C

I've encountered a problem trying to reduce the size of my code. What I was trying to do was passing either name or color to function writedata so that I wouldn't have to write it twice for each case.
typedef struct Pet{
char* name;
char* color;
} pet;
void writedata(pet *Pet, char string[], const char field[]){
gets(string);
Pet->field= (char*)malloc(strlen(string)+1);//I wanted it to be treated like Pet->name
strcpy(Pet->field, string);
}
The call of the function:
writedata(Pet, string, name);
I'm quite sure I got something wrong.
update: the whole code http://ideone.com/Y7L8Hu
update2: I tried to implement it using offset according to BLUEPIXY's advice but it seems I misunderstand manipulations with fields using their addresses... I believe the problem could be that the fields aren't initialized in the first place, but then again, my aim is to initialize them.
typedef struct Pet{
char* name;
int legs;
char* color;
} pet;
void writedata(pet *Pet, size_t FIELD){
char string[50];
gets(string);
(char*)Pet+offsetof(struct Pet, FIELD) = (char*)malloc(strlen(string)+1);//I wanted it to be treated like Pet->name
strcpy((char*)Pet+FIELD, string);
}
That's not how C works. However, I think using string comparison can achieve what you need:
if (strcmp(field, "name") == 0)
{
Pet->name = ...
}
else if (strcmp(field, "color") == 0)
{
Pet->color = ...
}
And call it with a string literal:
writedata(Pet, string, "name");
Using enum is also an option.
BTW, don't use gets, it's dangerous, use fgets instead.
use macro function sample.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define writedata(var, buffer, field) \
do {\
int len = strlen(buffer);\
var->field = (char*)malloc(len + 1);\
memcpy(var->field, buffer, len+1);\
}while(0)
typedef struct Pet{
char* name;
int legs;
char* color;
char* voice;
} pet;
void addpet(pet* Pet, int *TotalLegs){//Can not be used reference(&) in C
char buff[50];
int len;
puts("Input name");
scanf("%49[^\n]", buff);
writedata(Pet, buff, name);
puts("How many legs?");
scanf("%d%*c", &Pet->legs);
puts("What does it say?");
scanf("%49[^\n]%*c", buff);
writedata(Pet, buff, voice);
puts("_____\n");
*TotalLegs += Pet->legs;
}
int main(){
int TotalLegs = 0;
pet* Pet1 = (pet*)malloc(sizeof(pet));
addpet(Pet1, &TotalLegs);
pet* Pet2 = (pet*)malloc(sizeof(pet));
addpet(Pet2, &TotalLegs);
pet* Pet3 = (pet*)malloc(sizeof(pet));
addpet(Pet3, &TotalLegs);
//printf("%s\n", Pet1->name);
//printf("%s\n", Pet1->voice);
printf("The animals have %d legs\n", TotalLegs);
free(Pet1);free(Pet2);free(Pet3);
return 0;
}
A lot of things are wonky in your code.
What you wish to do can be achieved but it takes different type of code than you'd want to write.
What you really want to do is simply create a function that fills in a name and a color.
So here is the simplest way to do it:
typedef struct pet {
char *name;
char *color;
} pet_t;
pet_t * new_pet(const char *name, const char *color)
{
pet_t *p;
p = malloc(sizeof(pet_t));
if ( p == NULL )
return NULL;
p->name = strdup(name); /* allocate space and copy string */
p->color = strdup(color); /* allocate spance and copy string */
return p;
}
void delete_pet(pet_t *p)
{
if ( p-> name )
free(p);
if ( p->color)
free(color);
if ( p )
free(p);
}
int main() {
pet_t *p;
p = new_pet("Harry", "brown");
printf("%s is a %s pet\n", p->name, p->color);
delete_pet(p);
return 0;
}

Writing an array of structs to a binary file in C

I have an array of structs I would like to write to a binary file. I have a write.c program and a read.c program. The write.c program seems to be working properly but when I run the read.c program I get a segmentation fault. I'm new to C so It would be great if someone could look over my code for any obvious errors. I promise it's not too long :)
write.c:
#include <stdlib.h>
#include <stdio.h>
struct Person
{
char f_name[256];
char l_name[256];
int age;
};
int main(int argc, char* argv[])
{
struct Person* people;
int people_count;
printf("How many people would you like to create: ");
scanf("%i", &people_count);
people = malloc(sizeof(struct Person) * people_count);
int n;
for (n = 0; n < people_count; n++)
{
printf("Person %i's First Name: ", n);
scanf("%s", people[n].f_name);
printf("Person %i's Last Name: ", n);
scanf("%s", people[n].l_name);
printf("Person %i's Age: ", n);
scanf("%i", &people[n].age);
}
FILE* data;
if ( (data = fopen("data.bin", "wb")) == NULL )
{
printf("Error opening file\n");
return 1;
}
fwrite(people, sizeof(struct Person) * people_count, 1, data);
fclose(data);
return 0;
}
read.c:
#include <stdlib.h>
#include <stdio.h>
struct Person
{
char f_name[256];
char l_name[256];
int age;
};
int main(int argc, char* argv[])
{
FILE* data;
if ((data = fopen("data.bin", "rb")) == NULL)
{
printf("Error opening file\n");
return 1;
}
struct Person* people;
fread(people, sizeof(struct Person) * 1/* Just read one person */, 1, data);
printf("%s\n", people[0].f_name);
fclose(data);
return 0;
}
Thanks for the help!
struct Person* people;
This allocates just a pointer to struct, but you don't have any allocated space for actual struct contents. Either use malloc similarly to your write program, or try something like:
struct Person people;
fread(&people, sizeof(people), 1, data);
You need to allocate memory for the person first. Change: struct Person* people; into struct Person* people = malloc(sizeof(struct Person));. And don't forget to free your memory at the end: free(people);.
You either need to malloc memory into the pointer variable people before you do the fread, or (easier) just read directly into a local variable:
struct Person people;
fread(&people, sizeof(struct Person) * 1/* Just read one person */, 1, data);
You need to allocate space for the data you are reading in:
people = malloc(sizeof(*people)*numPeople);

Resources