String characters disappear on entering a function - c

I was tasked as an assignment to build a function to build an array of students and hold some values using a linked list. The task is to take some info from a txt file about said students, building said a university struct type which will hold an array of students and printing the info on a new txt file, now the max characters in the student string name can be 99 but must be dynamic allocated after giving the name length. All the other variables like integer values and characters are moved successfully but when is all done the string name in all the students just disappear upon (Changes to "Invalid characters in string as soon as I use the university struct) entering a different function.
The structs
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
typedef struct stud
{
char* name;
int long ID;
float mtmGrade;
char gradesStr[6];
char approved;
struct stud* next;
}stud;
typedef struct uni
{
stud* studentsArray;
int studentNum;
}uni;
void studentInput(uni* university, stud* students, FILE* in);
Main
void main()
{
FILE* in, * out;
uni* Uni = (uni*)malloc(sizeof(uni));
stud* students = NULL;
in = fopen("input.txt", "rt");
if (!in) Error_Msg("File not found!");
out = fopen("output.txt", "w");
if (!out) Error_Msg("File not found!");
Uni->studentsArray = (stud*)malloc(sizeof(stud)); // FREE THIS
studentInput(Uni, students, in);
fclose(in);
fclose(out);
}
The function
void studentInput(uni* university, stud* students, FILE* in)
{
stud* temp;
char maxName[99];
university->studentNum = 0;
while (feof(in) == 0)
{
university->studentsArray = (stud*)realloc(university->studentsArray, sizeof(stud));
university->studentNum++;
temp = (stud*)malloc(sizeof(stud));
// Name input //
fscanf(in, "%s", maxName);
temp->name = (char*)malloc((strlen(maxName) + 1) * sizeof(char));
if (!(temp->name)) Error_Msg("Not enough memory found");
temp->name = maxName;
// ID and matam grade input //
fscanf(in, "%d%f%s", &temp->ID, &temp->mtmGrade,temp->gradesStr);
temp->next = students;
students = temp;
}
university->studentsArray = students;
}

Please notice maxName is a local variable in function studentInput, it's only valid inside the function.
After:
temp->name = maxName;
temp->name will point to the local memory, and the content may be modified after the function exit.
So, you can use strcpy or strdup instead:
void studentInput(uni* university, stud* students, FILE* in)
{
stud* temp;
char maxName[99];
university->studentNum = 0;
while (feof(in) == 0)
{
university->studentsArray = (stud*)realloc(university->studentsArray, sizeof(stud));
university->studentNum++;
temp = (stud*)malloc(sizeof(stud));
// Name input //
fscanf(in, "%s", maxName);
temp->name = strdup(maxName);
if (!(temp->name)) Error_Msg("Not enough memory found");
// ID and matam grade input //
fscanf(in, "%d%f%s", &temp->ID, &temp->mtmGrade,temp->gradesStr);
temp->next = students;
students = temp;
}
university->studentsArray = students;
}
P.S.
use
int main()
better conforms to the C standard than
void main()

Related

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);

C segmentation fault 11 when implementing hashtable

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.

When freeing a pointer for a nested struct getting Segmentation fault

This is my code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define NAMESIZE 20
#define LINESIZE 1024
typedef struct name name;
struct name
{
char last[NAMESIZE]; /* last name */
char first[NAMESIZE]; /* first name*/
};
typedef struct record record;
struct record
{
name name;
int score;
};
typedef struct record_list record_list;
struct record_list
{
record *data; /* the dynamic array of records */
size_t nalloc; /* number of records allocated */
size_t nused; /* number of records in use */
};
void list_init(record_list *list)
{
list -> data = 0;
list -> nalloc = 0;
list -> nused = 0;
}
int list_insert(record_list *list, const record *rec)
{
size_t newSize;
record *tmp;
if(list -> nalloc == list -> nused)
{
if(list -> nalloc == 0)
{
newSize = 1;
}
else
{
newSize = 2 * list -> nalloc;
}
tmp = realloc(list -> data, newSize * sizeof(record));
if(tmp == 0)
{
return 0;
}
list -> data = tmp;
list -> nalloc = newSize;
}
list -> data[list -> nused++] = *rec;
return 1;
}
void list_destroy(record_list *list)
{
printf("Attempting Deletion");
free(list->data);
free(list->nalloc);
free(list->nused);
list -> data = 0;
list -> nalloc = 0;
list -> nused = 0;
}
int main(void){
record_list list;
record *r;
name n;
int score;
char input[NAMESIZE];
char name[NAMESIZE];
char lname[NAMESIZE];
list_init(&list);
while(input != NULL) {
printf("Please enter a value for Name: ");
scanf("%s", input);
strcpy(input, name);
printf("Enter last name: ");
scanf("%s", input);
strcpy(input, lname);
printf("Enter score: ");
scanf("%d", &score);
r=(record*)malloc(sizeof(record));
if(r == NULL){
printf("There isn't enough memory.\n");
}
strcpy(n.first, name);
strcpy(n.last, lname);
r -> name = n;
list_insert(&list, r);
printf("\n");
printf("Choose next action:\n");
printf("\tTo add more type \"add\";\n");
printf("\tTo delete all records type \"del\";\n");
scanf("%s", input);
if(strcmp(input, "del") == 0){
list_destroy(&list);
printf("Deleted");
break;
}
}
return 1;
}
I am working on a small lab exercise where we make a struct, fill it and clear it if the user needs to. Yesterday everything worked but today I seem to either have not saved it or broke something because I am getting a ton of errors.
Here is an example of the error I'm getting:
Essentially when I call a method
void list_destroy(record_list *list);
it crashes before reaching the first print statement which means I am doing something wrong with the method call.
Summarized question: What could be causing the segmentation fault (where am I accessing incorrect memory) Or how else can I clear my struct memory without using free?
Thank you very much.
This should tell what your problem is:
code.c: In function 'list_destroy':
code.c:74: warning: passing argument 1 of 'free' makes pointer from integer without a cast
code.c:75: warning: passing argument 1 of 'free' makes pointer from integer without a cast
You're trying to free int fields. You can't free them because they are not pointers to memory blocks.
So, remove these lines of code:
free(list->nalloc);
free(list->nused);

Read file .txt and save it to struct in c

I want to ask about file processing and struct in C language, I get an assignment from my lecture, and am so really confused about string manipulation in C programming. Here is the task.
get data from mhs.txt
store in struct
sort by name ascending
Here is the mhs.txt
1701289436#ANDI#1982
1701317124#WILSON#1972
1701331734#CHRISTOPHER STANLEY#1963
1701331652#SHINVANNI THEODORE#1962
1701331141#MUHAMMAD IMDAAD ZAKARIA#1953
1701331564#MARCELLO GENESIS DRIET J.#1942
1701322282#ANANDA AULIA#1972
1701329175#LORIS TUJIBA SOEJONOPOETRO#1983
1701301422#DEWI JULITA#1993
1701332610#HARRY HUTALIANG#1982
first before # is NIM,
after first # is name
and the last after #, is year
and here is what i've done
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student{
char nim[11];
char name[50];
int year;
}s[10];
int main(){
FILE *fp;
int c,i,n;
printf("Read mhs.txt...");
getchar();
fp = fopen("mhs.txt", "r");
c = getc(fp);
i = 0;
while(c!=EOF){
printf("%c", c);
c = getc(fp);
i++;
}
fclose(fp);
getchar();
return 0;
}
First thing, I could save data on struct, but in here I very confused to separate a string.
That's all I know about struct and file processing, is there anyone who can help me? I have traveled around the internet and could not find the correct results.
Sorry if there are duplicate questions, and sorry if my english is too bad.
This is pure C code, you should new three import function: strtok & qsort & fsan.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student
{
char nim[11];
char name[50];
int year;
};
#define BUFFER_SIZE 100
struct student saveToStruct (char* str)
{
struct student res;
int flag = 0;
char *token = strtok(str, "#");
while( token != NULL )
{
if (0 == flag)
strcpy(res.nim, token);
else if (1 == flag)
strcpy(res.name, token);
else
res.year = atoi(token);
flag++;
token = strtok( NULL, "#" );
}
return res;
}
void print(struct student* arr, int size)
{
for (int i = 0; i < size; i++)
{
printf("%s, %s, %d\n", arr[i].nim, arr[i].name, arr[i].year);
}
}
int cmp(const void* l, const void* r)
{
return strcmp(((const student*)l)->name, ((const student*)r)->name);
}
int main()
{
struct student arr[10];
FILE* file = fopen("mhs.txt", "r");
if (!file)
return -1;
char buffer[BUFFER_SIZE];
int flag = 0;
while (fgets(buffer, BUFFER_SIZE, file))
{
arr[flag] = saveToStruct(buffer);
flag++;
}
print(arr, 10);
qsort(arr, 10, sizeof(struct student), cmp);
printf("After sort by name!\n");
print(arr, 10);
return 0;
}
Since you've tagged this as C++, I'd use C++:
#include <iostream>
#include <string>
#include <algorithm>
struct student {
std::string nim;
std::string name;
int year;
bool operator<(student const &other) {
return name < other.name;
}
friend std::istream &operator>>(std::istream &is, student &s) {
std::getline(is, s.nim, '#');
std::getline(is, s.name, '#');
return is >> s.year;
}
};
int main() {
std::ifstream in("mhs.txt");
std::vector<student> students{
std::istream_iterator<student>(in),
std::istream_iterator<student>()
};
std::sort(students.begin(), students.end());
}
If you want to accomplish roughly the same thing in C, it's probably easiest to do the reading with fscanf using a scanset conversion, like:
fscanf(infile, "%10[^#]#%49[^#]#%d", student.nim, student.name, &student.year);
The scanset conversion gives you something like a subset of regular expressions, so the %[^#] converts a string of characters up to (but not including) a #. In this case, I've limited the length of each to one less than the length you gave for the arrays in your struct definition to prevent buffer overruns.
Then you can do the sorting with qsort. You'll need to write a comparison function, and doing that correctly isn't always obvious though:
int cmp(void const *aa, void const *bb) {
student const *a = aa;
student const *b = bb;
return strcmp(a->name, b->name);
}
Here are some hints, not the full answer. Hope it could help you.
first you need to read the file line by line, instead of character by character. You need the function of fgets(). you may find the reference from www.cplusplus.com/reference/cstdio/fgets/
second you can use strtok() to seperate strings. here is an example.
char str[] = "now # is the time for all # good men to come to the # aid of their country";
char delims[] = "#";
char *result = NULL;
result = strtok( str, delims );
while( result != NULL ) {
printf( "result is \"%s\"\n", result );
result = strtok( NULL, delims );
}
and you may find the reference to strtok() from http://www.cplusplus.com/reference/cstring/strtok/
third, use qsort() to sort the structure array. you may find the reference of it from http://www.cplusplus.com/reference/cstdlib/qsort/. examples can also be found there.

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