Invalid pointer in C - c

I apologize in advance, but this is kind of a long one.
In my program, I read in the student's information, but when I go to output it, it comes out scrambled, and then gets a pointer error.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student{
char *firstName;
char *lastName;
char id[10];
char gender;
int age;
double gpa;
};
void main()
{
int n;
struct student *classroom;
printf("How many students?");
scanf("%d",&n);
classroom = (struct student*) malloc(n*sizeof(struct student));
if (classroom == NULL)
exit(1);
readStudentsInformation(classroom,n);
outputStudents(classroom,n);
printf("The average age is %.2f.\n",averageAge(classroom,n));
printf("The average GPA is %.2f.\n",averageGpa(classroom,n));
sortByLastName(classroom,n);
outputStudents(classroom,n);
sortByID(classroom,n);
outputStudents(classroom,n);
sortByAge(classroom,n);
}
void outputStudents(struct student classroom[], int size)
{
int i;
for (i = 0; i < size; i++)
{
printf("%15s",classroom[i].firstName);
printf("%15s:",classroom[i].lastName);
printf("%14s,",classroom[i].id);
printf("%3c",classroom[i].gender);
printf("%5d",classroom[i].age);
printf("%5.2f",classroom[i].gpa);
}
}
Input:
How many students?2
First Name?Thom
Last Name?Arron
ID?2
Gender?M
Age?26
GPA?3.9
First Name?Frank
Last Name?Roberts
ID?1
Gender?F
Age?24
GPA?3.4'
Output:
Roberts Roberts: 2, M 26 3.90 : 1, F 24 3.40The average age is 25.00.
The average GPA is 3.65.
* glibc detected * ./lab12: munmap_chunk(): invalid pointer: 0x00007fff30319a90 *
: 2, M 26 3.90Aborted (core dumped)
The full code is here, but I didn't want to copy 200 lines to stack overflow: http://codepad.org/LYpS6t5z
Any idea what would cause this?

This is one conceptual error, you have it in a couple of places
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
classroom[i].firstName = temp;
What you want here instead is
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
strcpy(classroom[i].firstName, temp); // note this
Or, cleaned up a bit:
classroom[i].firstName = malloc(1+strlen(temp)); // note clean up here
if (classroom[i].firstName == NULL)
exit(1);
strcpy(classroom[i].firstName, temp);
Or even just
classroom[i].firstName = strdup(temp); // this takes place of all the lines above
These errors explain why your free's are failing.
Nothing else jumps out at me.

classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
classroom[i].firstName = temp;
Your second assignment here overwrites the address, leaking the mallocd memory and making the pointer invalid as soon as that for iteration finishes. The same buffer is reused (with the same error) for lastName, which is why you see Roberts Roberts instead of the actual first and last name. When you go to free them, they are (1) invalid and (2) not made by malloc, so you get the crash you see.
Just like other arrays, you can't copy them by assignment, you have to copy byte-by-byte:
size_t len = strlen(temp);
classroom[i].firstName = malloc(1+len);
if (classroom[i].firstName == NULL)
exit(1);
strncpy(classroom[i].firstname, temp, len);
classroom[i].firstname[len] = '\0';
And don't cast the result of malloc.

Weirdness happening in readStudentsInformation, in particular lines like
classroom[i].lastName = temp;
are causing issues later on when you try to free this memory with
free(classroom[i].firstName);
Appropriate memory handling below:
void readStudentsInformation(struct student classroom[], int size)
{
int i;
char temp[50];
for (i = 0; i < size; i++)
{
printf("First Name?");
scanf("%s",temp);
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
/* after mallocing good memory can write in the data.. */
strcpy(classroom[i].firstName, temp);
/* classroom[i].firstName = temp; */
printf("Last Name?");
scanf("%s",temp);
classroom[i].lastName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].lastName == NULL)
exit(1);
/* classroom[i].lastName = temp; */
strcpy(classroom[i].lastName, temp);
printf("ID?");
scanf("%s",classroom[i].id);
fflush(stdin);
__fpurge(stdin);
printf("Gender?");
scanf("%c",&classroom[i].gender);
printf("Age?");
scanf("%d",&classroom[i].age);
printf("GPA?");
scanf("%lf",&classroom[i].gpa);
}
}

Related

Unexpected breakpoint trying to print C string from struct

So I am having two problems with this piece of code. The first problem when I'm following break points through it and I'm trying to copy the array to the struct fields, it is saying Error reading character string. And the second problem I am having is with the output "printMovieInfo function it is not printing out my list and it is causing an "unexpected breakpoint"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#pragma warning(disable : 4996)
#pragma warning(disable : 6387)
typedef struct {
char* g;
char* t;
}MovieInfo;
#define kTenItemsEntered 10
#define kCharacterString 30
//prototypes
void eliminateEndOfLine(char* buffer);
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]);
void printMovieInfo(MovieInfo list[]);
int main() {
MovieInfo newMovieInfo[kTenItemsEntered];
char title[kCharacterString];
char genre[kCharacterString];
printf("Enter 10 Title of Movie and 10 Genres\n");
for (int i = 1; i <= kTenItemsEntered; i++) {
printf("%d:\n", i);
printf("Title: ");
fgets(title, kCharacterString,stdin);
eliminateEndOfLine(title);
printf("Genre: ");
fgets(genre, kCharacterString, stdin);
eliminateEndOfLine(genre);
getMovieInfo(newMovieInfo, title, genre);
}
printMovieInfo(newMovieInfo);
return 0;
}
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]) {
ptr->t = (char*)malloc(strlen(title) + 1);
if (ptr->t == NULL) {
printf("Error allocating the title in the struct\n");
}
ptr->g = (char*)malloc(strlen(genre) + 1);
if (ptr->g == NULL) {
printf("Error allocating the genre in the struct\n");
}
strcpy(ptr->t, title);
strcpy(ptr->g, genre);
}
void printMovieInfo(MovieInfo list[]) {
printf("%-35s %-35s\n", "Title", "Genre");
for (int i = 0; i < kTenItemsEntered; i++) {
printf("%-35s %-35s\n", list[i].t, list[i].g);
}
}
//This is the end of new line function from examples
void eliminateEndOfLine(char* buffer)
{
char* target = strchr(buffer, '\n');
if (target != NULL)
{
*target = '\0';
}
}
Output
The output does not print
I'm guessing you're actually compiling in C++, otherwise this wouldn't compile as you need to use struct MovieInfo, not MovieInfo in C. You can fix that by doing:
typedef struct MovieInfo {
char* g;
char* t;
} MovieInfo;
In main your newMovieInfo is an array of MovieInfo structs. When you call getMovieInfo(newMovieInfo, title, genre);, you're actually passing a pointer to the first element of the array to the function (the array 'decays' into a pointer to the first element). What you should do instead is pass a pointer to the element you actually want to fill in. Array indices start at 0, not 1, so you need to fix your loop as well:
// previously: for (int i = 1; i <= kTenItemsEntered; i++) {
for (int i = 0; i < kTenItemsEntered; i++) {
...
// previously: getMovieInfo(newMovieInfo, title, genre);
getMovieInfo(&newMovieInfo[i], title, genre);
}
Then in getMovieInfo, since you already have a valid pointer to the struct, you don't need to allocate space for a new one:
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]) {
// removed:
// ptr = (MovieInfo*)malloc(sizeof(MovieInfo));
// if (ptr == NULL) {
// printf("Error allocating the struct\n");
// }
ptr->t = (char*)malloc(strlen(title) + 1);
if (ptr->t == NULL) {
printf("Error allocating the title in the struct\n");
}
ptr->g = (char*)malloc(strlen(genre) + 1);
if (ptr->g == NULL) {
printf("Error allocating the genre in the struct\n");
}
strcpy(ptr->t, title);
strcpy(ptr->g, genre);
}
Finally in printMovieInfo, your current code is printing the first element's data multiple times. You can fix that by indexing into the array to get the element you want:
void printMovieInfo(MovieInfo list[]) {
printf("%-35s %-35s\n", "Title", "Genre");
for (int i = 0; i < kTenItemsEntered; i++) {
// previously: printf("%-35s %-35s\n",list->t,list->g);
printf("%-35s %-35s\n",list[i].t,list[i].g);
}
}
Each time you call getMovieInfo, you're storing the data in a local pointer to allocated memory. That pointer gets lost when the function returns, so newMovieInfo is never populated with anything.
The pointer being passed to the function points to an already existing instance of MovieInfo, so there's no need to allocate memory for it (though you still need to allocate memory for the strings).
Even after this fix, you're only ever updating the first element of the newMovieInfo. You need to pass a pointer to the array element in question when you call getMovieInfo:
getMovieInfo(newMovieInfo + i - 1, title, genre);
Note the -1 here because you're looping from 1 to kTenItemsEntered and array indices in C start at 0. If you changed the loop to go from 0 to kTenItemsEntered-1, you can remove the -1 from the line above.
Your printing function is also only printing the first element of the array, so change the line in the loop body to:
printf("%-35s %-35s\n",list[i].t,list[i].g);

Why is realloc giving me inconsistent behaviour?

I am currently taking a procedural programming course at my school. We are using C with C99 standard. I discussed this with my instructor and I cannot understand why realloc() is working for his machine, but it is not working for mine.
The goal of this program is to parse a text file students.txt that has students' name and their GPA formatted like this:
Mary 4.0
Jack 2.45
John 3.9
Jane 3.8
Mike 3.125
I have a function that resizes my dynamically allocated array, and when I use realloc the debugger in my CLion IDE, it gave me SIGABRT.
I tried using an online compiler and I get realloc(): invalid next size.
I have been trying to debug this all weekend and I can't find the answer and I need help.
My code is currently looking like this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITIAL_SIZE 4
#define BUFFER_SIZE 512
#define GRADE_CUTOFF 3.9
// ERROR CODES
#define FILE_OPEN_ERROR 1
#define MEMORY_ALLOCATION_ERROR 2
struct student {
double gpa;
char *name;
};
struct student *resizeAllocationIfNeeded(struct student *listOfStudents,
unsigned int studentCount, size_t *currentSize) {
if (studentCount <= *currentSize) {
return listOfStudents;
}
*currentSize *= 2;
struct student *resizedList = (struct student *) realloc(listOfStudents, *currentSize * sizeof(struct student));
if (resizedList == NULL) {
perror("Failed to allocate memory");
exit(MEMORY_ALLOCATION_ERROR);
}
return resizedList;
}
size_t getNamesAndGrades(FILE *file, struct student *listOfStudents, size_t size) {
unsigned int studentCount = 0;
char buffer[BUFFER_SIZE];
while(fscanf(file, "%s %lf", buffer, &listOfStudents[studentCount].gpa) > 0) {
listOfStudents[studentCount].name = strdup(buffer);
studentCount++;
listOfStudents = resizeAllocationIfNeeded(listOfStudents, studentCount, &size);
}
return studentCount;
}
void swapStudents(struct student *listOfStudents, int x, int y) {
struct student temp = listOfStudents[x];
listOfStudents[x] = listOfStudents[y];
listOfStudents[y] = temp;
}
void sortStudentsByGPA(struct student *listOfStudents, unsigned int studentCount) {
for (int i = 0; i < studentCount; i++) {
for (int j = 0; j < studentCount - i - 1; j++) {
if (listOfStudents[j].gpa < listOfStudents[j + 1].gpa) {
swapStudents(listOfStudents, j, j + 1);
}
}
}
}
void printStudentAndGPA(struct student *listOfStudents, unsigned int studentCount) {
for (int i = 0; i < studentCount; i++) {
if (listOfStudents[i].gpa > GRADE_CUTOFF) {
printf("%s %lf\n", listOfStudents[i].name, listOfStudents[i].gpa);
}
free(listOfStudents[i].name);
}
}
void topStudents(char *fileName) {
FILE *file = fopen(fileName, "r");
if (!file) {
perror("Could not open file for reading");
exit(FILE_OPEN_ERROR);
}
struct student *listOfStudents = (struct student *) malloc(INITIAL_SIZE * sizeof(struct student));
if (listOfStudents == NULL) {
perror("Failed to allocate memory");
exit(MEMORY_ALLOCATION_ERROR);
}
unsigned int studentCount = getNamesAndGrades(file, listOfStudents, INITIAL_SIZE);
sortStudentsByGPA(listOfStudents, studentCount);
printStudentAndGPA(listOfStudents, studentCount);
free(listOfStudents);
}
int main() {
topStudents("students.txt");
return 0;
}
You have a fencepost error when checking whether you need to resize the array.
Your initial allocation size is 4, which means that the highest valid index is 3.
In the loop in getNamesAndGrades(), after you read into listOfStudents[3] you increment studentCount to 4. Then you call resizeAllocationIfNeeded(listOfStudents, studentCount, &size);
Inside resizeAllocationIfNeeded(), studentCount == 4 and *currentSize == 4. So the test
if (studentCount <= *currentSize) {
return listOfStudents;
}
succeeds and you return without calling realloc().
Then the next iteration of the loop assigns to listOfStudents[4], which causes a buffer overflow.
You need to change that condition to studentCount < *currentSize.
There are two errors in your code: one is just a typo, the other is a more serious logical error.
First, you are reallocating too late, because of the condition in resizeAllocationIfNeeded(). When studentCount == currentSize, this doesn't resize (even though it should), which makes you overflow the array of students and causes problems.
You can change the condition to fix this:
if (studentCount < *currentSize) {
return listOfStudents;
}
Apart from the above, your main error is in getNamesAndGrades(), where you are reallocating memory and assigning the new pointers to a local variable. You then use that variable in topStudents() as if it was updated. This will of course not work, as the initial pointer passed by topStudents() becomes invalid after the first realloc() and memory is irrevocably lost when getNamesAndGrades() returns.
You should either pass a pointer to the student array, or better just make the function create the array for you.
Here's a solution, renaming getNamesAndGrades to getStudents:
struct student *getStudents(FILE *file, unsigned int *studentCount) {
char buffer[BUFFER_SIZE];
struct student *listOfStudents;
size_t size = INITIAL_SIZE;
*studentCount = 0;
listOfStudents = malloc(size * sizeof(struct student));
if (listOfStudents == NULL) {
perror("Failed to allocate memory");
exit(MEMORY_ALLOCATION_ERROR);
}
while(fscanf(file, "%511s %lf", buffer, &listOfStudents[*studentCount].gpa) == 2) {
listOfStudents[*studentCount].name = strdup(buffer);
(*studentCount)++;
listOfStudents = resizeAllocationIfNeeded(listOfStudents, *studentCount, &size);
}
return listOfStudents;
}
// ...
void topStudents(char *fileName) {
FILE *file = fopen(fileName, "r");
if (!file) {
perror("Could not open file for reading");
exit(FILE_OPEN_ERROR);
}
unsigned int studentCount;
struct student *listOfStudents = getStudents(file, &studentCount);
sortStudentsByGPA(listOfStudents, studentCount);
printStudentAndGPA(listOfStudents, studentCount);
free(listOfStudents);
}
int main() {
topStudents("students.txt");
return 0;
}
Additional notes:
When scanning on a fixed size buffer (in this case 512 bytes), use %511s, not just %s, that's a buffer overflow waiting to happen.
You are scanning two fields, so check if fscanf's return value is == 2, not > 0, you don't want for example one field initialized and one not.
Don't cast the result of malloc() or realloc()
For the future, if you are on Linux, compiling with gcc -g -fsanitize=address will give you detailed error reports when something goes bad in the heap, telling you exactly where memory was allocated, freed and used.

I lose the values in a struct (c)

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define stock_dir "/Users/myname/prices/"
#define file_list "/Users/myname/trade/trade/nasdaq100_stock_list.txt"
#define look_back_period 3
#define num_stocks 103
#define days_of_data 21
int main()
{
FILE *stocks, *stk;
char stock[11], fullpath[50] = "\0", header[25];
char line_of_data[40];
char *sclose, *svol;
int n = 0, i = 0;
typedef struct daily_data {
char *date;
float close;
int vol;
}data;
sclose = (char*) malloc(20*sizeof(char));
svol = (char*) malloc(20*sizeof(char));
data** day_data = (data**) malloc(num_stocks*sizeof(data*) );
if (day_data == NULL)
{
printf("day_data not allocated\n");
exit(0);
}
for(i = 0; i < num_stocks; i++)
if ((day_data[i] = (data*)malloc(days_of_data*sizeof(data))) == NULL)
{
printf("data[%d] not allocated\n", i);
exit(0);
}
for(i = 0; i < num_stocks; i++)
for(n = 0; n < days_of_data; n++)
if ((day_data[i][n].date = (char*)malloc(20)) == NULL)
{ printf("data[%d][%d] not allocated\n", i,n);
exit(0);
}
/* ... code omitted ... */
if ( (stocks = fopen(file_list, "r") )== NULL)
printf("didn't open file list\n");
i = 0;
while (fgets(stock, sizeof(stock), stocks) != NULL)
{
printf("%s",stock);
strcpy(fullpath,stock_dir);
strcat(fullpath,stock);
fullpath[strcspn(fullpath, "\n")] = 0;
if ( (stk = fopen(fullpath, "r") )== NULL)
printf("didn't open quote list\n");
fgets(header,sizeof(header),stk);
n=0;
while(fgets(line_of_data, sizeof(line_of_data),stk) !=NULL)
{
fgets(line_of_data,sizeof(line_of_data),stk);
day_data[i][n].date = strtok(line_of_data, ",");
sclose = strtok(NULL,",");
day_data[i][n].close = atof(sclose);
svol = strtok(NULL, ",");
day_data[i][n].vol = atoi(svol);;
printf("%s %f %d\n",day_data[i][n].date,day_data[i][n].close,day_data[i][n].vol);
n++;
}
fclose(stk);
i++;
}
for (n = look_back_period - 1; n < (days_of_data - look_back_period); n++)
printf("%d %s %f %d\n",n, day_data[1][n].date, day_data[1][n].close, day_data[1][n].vol);
}
The print statement in the while(fgets(line_of_data, sizeof(line_of_data),stk) !=NULL) loop shows that everything went into the right place. But when I print values outside they're mostly wrong. I'm supposed to add more details but I don't know what else to say. I lose the values in the struct when I leave the loop.
You overwrite the same data again and again.
Take a look at your structure:
typedef struct daily_data {
char *date; ///< a pointer without own storage
float close;
int vol;
}data;
while processing your file you read each line into line_of_data
while(fgets(line_of_data, sizeof(line_of_data),stk) !=NULL)
you tokenize the line_data and assign the pointer to data->date
day_data[i][n].date = strtok(line_of_data, ",");
What tokenize (strtok reference) does is inserting terminators into your input string and returning the pointer to the start of the new part of your input. So no new memory is allocated at this point. the returned pointer points into your input string.
So effectively you assigning the local variable pointer to your data storage structure.
Additionally to this you lose the pointer to your initially allocated memory for the date pointer.
I would suggest you to remove the a priory allocation of date and allocate the required memory at the point you really know the required length or if you are sure, you know the maximum length, then you can just make the date member an array.
So you either have to allocate new memory and copy the tokenized data or if you made date a fixed size array, just copy the tokenized data.
on the first variant it would look like this
char * tok = strtok(line_of_data, ",");
day_data[i][n].date = malloc(strlen(tok)+1);
strcpy(day_data[i][n].date, tok);
(+ remove the pre allocation of the date member)
or the second variant:
change data to
typedef struct daily_data {
char date[20];
float close;
int vol;
}data;
and the processing code looks like this:
char * tok = strtok(line_of_data, ",");
strcpy(day_data[i][n].date, tok);
(+ (of course) remove the pre allocation of the date member)
You also should in any case add error handling if the tokenized string exceeds the max length or the format of the lines does not match the expectation (missing delimiters, wrong/invalid number(formats), ...).

Seg Fault in my

I have an issue with my code here when I try to add a record to the database. Everytime I enter anything more than a few characters in for the first name prompt, the program will seg fault, even after moving elements from the old record. I understand that seg faults are due to trying to access memory that we don't have access too, but for the life of me I can't figure out what's causing it. The point of this code is to create a structure of type credit card which is defined in the header file which has a hardcoded set of 4 records, and be able to print the current database, add a record, delete a record, and other choices from the menu. Right now I am focusing on adding and deleting a record, as they are the most difficult part of this program. Why do I keep getting seg fault errors when trying to add a record?
The header file:
#ifndef myStruct
#define myStruct
struct creditCard
{
char firstName[100];
char lastName[100];
char cardNumber[17]; //TA advised to use string since 16 digits will overflow in plain integer
char expMonth[10];
};
//function headers below
int printCreditCard(struct creditCard *);
int sizeOfDb(struct creditCard *);
int countRecords(struct creditCard *);
int deleteRecord(struct creditCard *);
int addRecord(struct creditCard *);
#endif
Here is the actual program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "myStruct.h"
int accesses = 0; //number of times database was manipulated
int count = 0; //used to count how many records there are; we always start at 4
//int size = 0; //used to tell us how big the database is
struct creditCard *ptr; //chunk of memory for each record/structure
struct creditCard *headPtr; //chunk of memory for the first records/structure
int main(void)
{
ptr = (struct creditCard *) malloc(4 * sizeof(struct creditCard));
headPtr = ptr;
memcpy(ptr->firstName,"Bob",100);
memcpy(ptr->lastName,"Marley",100);
memcpy(ptr->cardNumber,"0000000000000000",17);
memcpy(ptr->expMonth,"September", 10);
ptr++;
count = count++;
memcpy(ptr->firstName, "John", 100);
memcpy(ptr->lastName, "Adams", 100);
memcpy(ptr->cardNumber,"1111111111111111",17);
memcpy(ptr->expMonth, "October", 10);
ptr++;
count = count++;
memcpy(ptr->firstName, "Bill", 100);
memcpy(ptr->lastName, "Gates", 100);
memcpy(ptr->cardNumber,"2222222222222222",17);
memcpy(ptr->expMonth, "January", 10);
ptr++;
count = count++;
memcpy(ptr->firstName, "Steve", 100);
memcpy(ptr->lastName, "Jobs", 100);
memcpy(ptr->cardNumber,"3333333333333333",17);
memcpy(ptr->expMonth, "May", 10);
count = count++;
while (1)
{
//headPtr = ptr; //put the newest database into headPtr so it points to the first record in that database
//ptr = headPtr; //start the database back at the first record
//countRecords(ptr); //update the count of the current database
int sel;
printf("MAIN MENU\n");
printf("=====\n");
printf("1. Select 1 to print all records.\n");
printf("2. Select 2 to print number of records .\n");
printf("3. Select 3 to print size of database.\n");
printf("4. Select 4 to add record.\n");
printf("5. Select 5 to delete record.\n");
printf("6. Select 6 to print number of accesses to database.\n");
printf("7. Select 7 to Exit.\n");
printf("Enter Your Selection: \n");
scanf("%d", &sel); //get user input;
if (sel == 1)
{
printCreditCard(ptr);
accesses++;
}
else if (sel == 2)
{
fprintf(stderr,"Number of records in the database is %d records\n", count); //pulls value of count from global updated variable
accesses++;
}
else if (sel == 3)
{
sizeOfDb(ptr);
accesses++;
}
else if (sel == 4)
{
ptr = headPtr;
addRecord(ptr);
accesses++;
}
else if (sel == 5)
{
deleteRecord(ptr);
accesses++;
}
else if (sel == 6)
{
fprintf(stderr,"Number of accesses to the database is %d\n", accesses);
accesses++;
}
else if (sel == 7)
{
printf("Now Exiting.\n");
return 0;
}
else
{
printf("Invalid input, please select a valid option.\n");
break; //go back to the main menu
}
}
}
//functions defined below
int sizeOfDb(struct creditCard *card2)
{
int size = 0;
int j;
for (j = 1; j <= count; j++)
{
size += sizeof(card2->firstName); //get the size of each element
size += sizeof(card2->lastName);
size += sizeof(card2->cardNumber);
size += sizeof(card2->expMonth);
card2++;
}
//loop through each record and get sizeof() of each record
fprintf(stderr, "Total Size of the Database is %d bytes.\n", size);
return size;
}
int addRecord(struct creditCard *card3)
{
count = count++;
fprintf(stderr, "count is %d \n", count);
int p;
struct creditCard *tempStruct;
//struct creditCard *dummy;
char fName, lName, month, number;
//tempStruct = (struct creditCard *) malloc (count * sizeof(struct creditCard)); //allocate memory to a dummy record
tempStruct = (struct creditCard *) malloc ((count+1) * sizeof(struct creditCard)); //allocate memory to a dummy record
//dummy = (struct creditCard *) malloc (sizeof(struct creditCard));
//dummy = (struct creditCard *) malloc (2 * sizeof(struct creditCard));
card3 = headPtr; //start at the beginning of the old database
for (p = 1; p < count; p++) //copies the old database in the new database up to the record before the newly allocated record
{
memcpy(tempStruct->firstName, card3->firstName, 100);
memcpy(tempStruct->lastName, card3->lastName, 100);
memcpy(tempStruct->cardNumber, card3->cardNumber, 17);
memcpy(tempStruct->expMonth, card3->expMonth, 10);
fprintf(stderr, "first name is %s\n", tempStruct->firstName);
if (p == count-1)
{
tempStruct++;
}
else
{
tempStruct++;
card3++;
}
}
printf("Please enter your first name.\n");
scanf("%s", &fName);
fprintf(stderr, "fname is %s\n", &fName);
memcpy(tempStruct->firstName, &fName, 100);//put first name in struct
//memcpy(dummy->firstName, &fName,100);//put first name in struct
fprintf(stderr, "struct name is %s\n", tempStruct->firstName);
//fprintf(stderr, "dummy name is %s\n", dummy->firstName);
printf("Please enter your last name.\n");
scanf("%s", &lName);
memcpy(tempStruct->firstName, &fName, 100);//put first name in struct
//memcpy(dummy->lastName,&lName,100);//put last name in struct
printf("Please enter your 16 digit credit card number with no spaces.\n");
scanf("%s", &number);
memcpy(tempStruct->firstName, &fName, 100);//put first name in struct
//memcpy(dummy->cardNumber,&number,17);//put creditcard number in struct
printf("Please enter the month in which your credit card expires.\n");
scanf("%s", &month);
memcpy(tempStruct->firstName, &fName, 100);//put first name in struct
//memcpy(dummy->expMonth,&month, 10); //put month of expiration in struct
//code below copies stuff from the dummy record to the new database called tempStruct
//memcpy(tempStruct->firstName, dummy->firstName, 100);
//memcpy(tempStruct->lastName, dummy->lastName, 100);
//memcpy(tempStruct->cardNumber, dummy->cardNumber, 17);
//memcpy(tempStruct->expMonth, dummy->expMonth, 10);
card3 = tempStruct; //put the new database in place of the old database
//free(dummy); //clear memory for the dummy record because we don't need it anymore
return 0;
}
int deleteRecord(struct creditCard *card4) //goes to the last record in the database and clears the memory for it, essentially deleting it
{
count = count--;
int l;
struct creditCard *newDb; //will hold the new database with one less record at the end
newDb = (struct creditCard *) malloc(count * sizeof(struct creditCard));
for (l = 0; l < 4; l++)
{
memcpy(newDb->firstName,card4->firstName,100);
memcpy(newDb->lastName,card4->lastName,100);
memcpy(newDb->cardNumber,card4->cardNumber,17);
memcpy(newDb->expMonth,card4->expMonth,10);
card4++;
}
//now we need to put the data into the new record
card4 = newDb; //put the new database into ptr to hold as the new database
return 0;
}
int printCreditCard(struct creditCard *card)
{
card = headPtr; //start at the beginning of the database
int i;
if (count == 0)
{
printf("The database is empty\n");
return 0;
}
else
{
for (i = 1; i <= count; i++)
{
printf("Credit Card Record %d\n", i);
fprintf(stderr, "First Name = \%s\n", card-> firstName);
fprintf(stderr, "Last Name = \%s\n", card-> lastName);
fprintf(stderr, "Card Number = \%s\n", card-> cardNumber);
fprintf(stderr, "Expiration Date = \%s\n\n", card-> expMonth);
card++; //go to the next record to print
}
}
return 1; //we have now printed all records, go to main menu.
}
There is really a lot wrong here-- you should try using lint or another static analysis tool to help you find errors.
One particular problem -- when you input fname, you are passing scanf a pointer to a single character-- you really want to pass it a pointer to an array of characters (actually, you don't want to use scanf at all, but I won't get into that). Scanf is merrily copying the input characters into the memory positions after that single char, which of course will cause a segfault at some point.
You'll also need to learn to use some sort of debugger (gdb?) which will help you look at the core dump when you get a segfault so you can find where things went wrong.

How to loop through data file to fill in structs?

Program specifications:
Read questions from a data file in the following format:
Question
Number of choices
N-amount of choices
Correct answer
Example:
What is the capital of France?
3
Madrid
Sydney
Paris
Paris
Present the user a question at a time and keep track of their score, etc, etc.
What I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINE_SIZE 60
#define MAX_LIST_SIZE 15
#define MAX_QUIZ_SIZE 10
typedef struct question {
char *question;
char **choices;
int n_choices;
char *correct_answer;
} QUESTION;
typedef struct quiz {
struct question *questions;
int n_questions;
} QUIZ;
char *dupString(const char *s) {
// copies a string
char *dup = malloc(strlen(s) + 1);
strcpy(dup, s);
return dup;
}
void free_choices(QUESTION *q) {
// free memory
for(int i = 0; i < q->n_choices; i++) {
free(q->choices[i]);
}
free(q->choices);
}
int ask(QUESTION *q) {
// Return 1 for correct guess, 0 for incorrect guess.
int choice;
// Print the question
printf("\n%s\n", q->question);
// Print the choices
for (int i = 0; i <= q->n_choices-1; i++) {
printf("%d : %s", i+1, q->choices[i]);
}
// Get user guess
do {
printf("Select an answer [1-%d]: ", q->n_choices);
scanf("%d", &choice);
/* Not sure how to structure here*/
if (strcmp(q->choices[choice-1], q->correct_answer) == 0) {
// if correct return 1
return 1;
}
} while (choice < 1 || choice > q->n_choices);
// Incorrect
return 0;
}
struct question parseQuestion(FILE *pData) {
int qIndex, numChoices;
char question[MAX_LINE_SIZE], temp[MAX_LINE_SIZE], choices[MAX_LINE_SIZE], correctAns[MAX_LINE_SIZE];
QUESTION q = {NULL, NULL, 0, NULL};
// Eat first line = QUESTION
fgets(question, MAX_LINE_SIZE, pData);
q.question = question;
// Eat second line = NUMBER OF CHOICES
fgets(temp, MAX_LINE_SIZE, pData);
numChoices = atoi(temp);
q.n_choices = numChoices;
// Allocate memory
q.choices = calloc(q.n_choices, sizeof(char*));
// Eat nth lines = CHOICES
for (qIndex=0; qIndex<=numChoices-1; qIndex++) {
fgets(choices, MAX_LINE_SIZE, pData);
q.choices[qIndex] = dupString(choices);
}
// Eat nth + 1 line = CORRECT ANSWER
fgets(correctAns, MAX_LINE_SIZE, pData);
q.correct_answer = correctAns;
return q;
}
int main() {
int num = 0; // question being asked
int strikes = 0; // incorrect guesses
FILE* pData;
char *filename = "tickle.txt";
char c;
if ((pData = fopen(filename, "r"))) {
printf("Welcome to the 2014 Quiz-festival!\n\n");
printf("Are you ready to begin? [Y/y]\n");
c = getchar();
if (c == 'Y' || c == 'y') {
QUESTION question = parseQuestion(pData);
ask(&question);
free_choices(&question);
} else {
printf("Come back again.\n");
return 0;
}
} else {
printf("File failed to open.");
}
fclose(pData);
return 0;
}
Thank you to #alk how picked up my error, that is resolved.
What I still can't get is how to loop through the data file and populate the quiz structure with question structures.
So this is where I'm struggling at the moment. From what I can tell I'm pretty close to finishing this little program as long as I can get this to work.
parseQuestion() duplicates the choices but misses to duplicate the question as well as the answer.
Instead it simply copies the two arrays' addresses to the locally defined variable QUESTION q which is copied on return.
The memory for the question and answer strings is free'd on returning from the function, accessing it afterwards invokes undefined behaviuor.

Resources