Cannot resolve EXC_BAD_ACCESS (code=EXC_I386_GBFLT) - c++

I'm very new at C++, maintaining my M.Sc on Information Systems. I have my first C++ homework announced and have been working on it for a couple of days. The aim of the code is simply to read info from a text file and print it on screen, then make calculation on some of it and print results to a new text file. However when I build, it gives the error:
EXC_BAD_ACCESS (code=EXC_I386_GBFLT)
in the first fscanf in readInfo function.
I know the code I wrote is not completely efficient but I just want it to print on screen and on output file correctly. I would really appreciate if anyone help me to resolve this error. I am about to freak out...
#include <stdio.h>
typedef struct {
char id[10];
char name[40];
float midterm;
float final;
int attendance;
}Student;
void readInfo(Student studentList[], int *count)
{
FILE *fin=fopen("scores.txt","r");
char surname = '\0';
*count=0;
while(!feof(fin))
{
fscanf(fin,"%c %c %c %f %f %d",studentList[*count].id, studentList[*count].name, &surname, &studentList[*count].midterm, &studentList[*count].final, &studentList[*count].attendance);
strcpy(studentList[*count].name, studentList[*count].name);
strcat(studentList[*count].name, " ");
strcat(studentList[*count].name, &surname);
*count++;
}fclose(fin);
printf("%-7s%17s %5.1f %5.1f %-2d\n", studentList[*count].id, studentList[*count].name, studentList[*count].midterm, studentList[*count].final, studentList[*count].attendance);
}
float studentScore(float midterm, float final, int attendance)
{
float score;
int maxAttend=0;
char id[10];
char name[40];
char surname[40];
FILE *fin=fopen("scores.txt","r");
while(!feof(fin))
{
fscanf(fin,"%c %c %c %f %f %d",id, name, surname, &midterm, &final, &attendance);
if(attendance>maxAttend)
maxAttend=attendance;
}fclose(fin);
score=midterm*0.3+final*0.5+(maxAttend/20)*attendance;
return score;
}
float avgScore(Student studentList[])
{
float average;
int count;
int totalScore=0;
readInfo(studentList, &count);
for(int i=0; i<=count; i++)
{
totalScore+=studentScore(studentList[count].midterm, studentList[count].final, studentList[count].attendance);
}
average=totalScore/count;
return average;
}
void courseGradeOutput(Student studentList[])
{
FILE *fout=fopen("output.txt","w");
int count;
int pass=0;
float score;
char letterGrade[2];
float avg;
fprintf(fout,"\tId\tName, Surname = (Score, Letter)\n");
readInfo(studentList, &count);
for(int i=0; i<=count; i++)
{
score=studentScore(studentList[i].midterm, studentList[i].final, studentList[i].attendance);
if(score>=0 && score<=49)
{ letterGrade[0]={'F'};
letterGrade[1]={'F'};}
else if (score>=50 && score<=59)
{letterGrade[0]={'F'};
letterGrade[1]={'D'};}
else if (score>=60 && score<=64)
{letterGrade[0]={'D'};
letterGrade[1]={'D'};}
else if (score>=65 && score<=69)
{letterGrade[0]={'D'};
letterGrade[1]={'C'};}
else if (score>=70 && score<=74)
{letterGrade[0]={'C'};
letterGrade[1]={'C'};}
else if (score>=75 && score<=79)
{letterGrade[0]={'C'};
letterGrade[1]={'B'};}
else if (score>=80 && score<=84)
{letterGrade[0]={'B'};
letterGrade[1]={'B'};}
else if (score>=85 && score<=89)
{letterGrade[0]={'B'};
letterGrade[1]={'A'};}
else if (score>=90 && score<=100)
{letterGrade[0]={'A'};
letterGrade[1]={'A'};}
if(score>=60)
pass++;
fprintf(fout,"%7s %16s = ( %4.1f, %6s\n)", studentList[i].id, studentList[i].name, score, letterGrade);
}
avg=avgScore(studentList);
fprintf(fout,"\nSome statistics:\n\nClass Avg Score: %5.2f \n #Students: %11d \n #Passed Students: %4d \n #Failed Students: %4d",avg,count,pass,(count-pass));
fclose(fout);
}
int main()
{ Student studentList[100];
int count;
readInfo(studentList, &count);
courseGradeOutput(studentList);
}
Screenshot

The crash is most likely caused by either fscanf followed by strcpy and/or by your increment of count inside readInfo.
You write *count++, but this is equivalent to
count = count + 1;
*(count-1);
What you want is (*count)++.
The fscanf scans characters %c where you want to scan strings %s (for id and name). You also want to scan surname as a string, probably, but then you need to change surname to be a character array:
char surname[30];
*count=0;
while(!feof(fin))
{
fscanf(fin,"%s %s %s %f %f %d",studentList[*count].id, studentList[*count].name, surname, &studentList[*count].midterm, &studentList[*count].final, &studentList[*count].attendance);
strcat(studentList[*count].name, " ");
strcat(studentList[*count].name, surname);
(*count)++;
}
fclose(fin);
I also removed the first strcpy as you were copying from a buffer to itself, which is not allowed.
I haven't checked the other functions thoroughly, but I do notice that you do not use the result from your readInfo called from main when you do courseGradeOutput: this function calls readInfo again. You could modify it to take the read student records and the count, so you don't have to read the file again.
You might also want to improve scanf a bit to pass a width, such as %29s for name, to avoid overflowing the buffer when the name inside the file is too long; and the same for the other strings you scan. You should then also look at the return value of fscanf and only use the things you scanned when it succeeded in scanning every argument.

Related

C File Handling - Trouble using Strings with Char

Good day, I'm having some trouble with my code. I just started with file handling last week and I'm currently stuck with adding Strings to the program. I would like to add the names in the main program from the file, but I've tried pretty much everything. Any help would be highly appreciated. Also, this is my first time on Stack Overflow, so sorry if I missed posting any information.
The picture includes the Input.txt file as well as the current output. I added console output to test the problem and it seems to have something to do with the characters. I've tried using String itself, but that was still a fail. Also, I'm not allowed to use the C++ way of file handling (if that makes sense). [I am allowed to use C++ though] - Doing this whole program for practice for my upcoming exams.
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
using namespace std;
int ReadFile(char*,int*,double*);
int main()
{
char names[128];
int ages[128];
double salaries[128];
int size = ReadFile(names, ages, salaries);
for(int i = 0; i < size; i++)
{
printf("My name is %s, and I am %d years old. My salary is $%.2lf\n", *(names+i), *(ages+i), *(salaries+i));
}
return 0;
}
int ReadFile(char *namesArr, int *ageArr, double *salaryArr)
{
FILE *IN = fopen("Input.txt", "r");
int i = 0;
if(IN == NULL)
{
cout << "Error! Can't open file.";
exit(1);
}
else
{
char name[20];
int age = 0;
double salary = 0.00;
while(fscanf(IN, " %c,%d,%lf", &name, &age, &salary) != EOF)
{
*(namesArr + i) = *name;
*(ageArr + i) = age;
*(salaryArr + i) = salary;
cout << *(namesArr+i) << " ";
i++;
}
}
fclose(IN);
return i;
}
Input.txt:
Kazu,21,2250.00 Anonymous,34,3500.25 John,31,2750.00 Paul,25,3125.25
Jin,19,1750.00
Save time, enable all compiler warnings
My name is %s --> My name is %c
If you want to read in the name, and not just the first letter, many more changes needed too such as:
char name[20];
int age = 0;
double salary = 0.00;
while(fscanf(IN, " %19[A-Za-z'] ,%d ,%lf", &name, &age, &salary) == 3)
{
*(namesArr + i) = *name; // This only copies the first `char`
strcpy(TBD, name); // Need something like this.
// TBD is a pointer to an `char` array
*(ageArr + i) = age;
*(salaryArr + i) = salary;
cout << TBD << " ";
i++;
}

How do I fix this bug?

I am making an application for playing Dungeons & Dragons, just for fun, and I ran into a problem. I've written multiple printfs and somehow it stopped working. When it finishes the first calculation and when it has to move on to the next, then it stops. I have no idea why.
This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctime>
#include <string>
int main(){
//variables
int dmgDice;
char entityName, dmgType;
int initiative, dmgBonus, damage, userAC, userHp;
//user input
printf("Type in your armor class: \n");
scanf("%d", &userAC);
printf("Type in your hit points : \n");
scanf("%d", &userHp);
printf("type in your initative: \n");
scanf("%d", &initiative);
printf("type in you damage bonus: \n");
scanf("%d", &dmgBonus);
printf("type in you damage type: \n");
scanf("%s", &dmgType);
printf("your damage dice: \n");
scanf("%d", &dmgDice);
printf("Entity you want to damage: \n");
scanf("%s", &entityName);
//d20 roll
srand((unsigned)time(0));
printf("d20 roll: \n");
int d20roll = (rand() % 20);
int SUM = d20roll + initiative;
printf("you have rolled SUM %d + %d = %d", d20roll, initiative, SUM);
if(d20roll > 14){
printf("\nYou've passed the enemies AC, now roll for damage!");
printf("\nYou did %d damage to the %s!", damage, entityName);
}
else{
printf("\nYou have missed, now %s attacks..", entityName);
int entityd20roll = (rand() % 20) + 1;
printf("\n%s passed your armor class, now it will roll for damage");
if(entityd20roll > userAC){
int edmg = (rand() % 12);
int hp = userHp - edmg;
if(hp < 0)
hp *= -1;
printf("\nhit points taken: \n");
printf("%s has dealt %d damge", entityName, edmg);
}
else{
printf("%s has missed you", entityName);
}
}
return 0;
}
Also, how can I create a memory file so the user doesn't have to type in everything over and over again?
I suggest including the library and trying the same thing with cout<< and cin>> because these commands work a little different.
So for example printf("Type in your armor class: \n");
becomes cout<<"Type in your armor class: "<<endl;
And scanf("%d", &userAC); becomes cin>>userAC;
And for your file saving system, I suggest you follow a class on file I/O like for example this video: https://www.youtube.com/watch?v=Iho2EdJgusQ.
You can then write all the user's options to a file and then read the information when the programs starts. This way the user's preferences will be kept.
I suggest (as #samu_242 already has done) to use the std::cout and std::cin commands for the inputs.
In your code, the printf
printf("\n%s passed your armor class, now it will roll for damage");
expects a string (%s), but you did not pass nothing. Moreover, maybe you had in mind to use array of chars, but you allocated memory for just one char:
char entityName, dmgType;
In this way, entityName will get only the first char that the user gives in input (e.g., if he/she types "Goblin" entityName will have only "G"). To declare an array of char, use
char entityName[N];
where N is the maximum length of the array. There are also ways to dynamically allocate memory, but my advise is to use the std::string.

Debug my reimplementation of strtok and split

I'm supposed to be writing code that takes a string of comma separated values without spaces (ex. my,name,is,jack). First we had to write a function
string nextstring(string str, int start_index)
that returns a single "value" from your initial string depending on the start index. The second part of the problem was to write a function
int split(string str, string a[], int max_size)
that will identify all the values in the initial string and put them in a string array and then return the total number of values stored in the array; i.e. if you had initially input my,name,is it would return 3.
My function never returns the correct value and whatever it returns changes depending on what the length of the words are.
#include <iostream>
#include <string>
using namespace std;
string nextstring(string str, int start_index);
int split(string str, string a[], int max_size);
int main()
{
string str;
int cnt;
string a[100];
cout<< "what is your string" << endl;
getline(cin, str);
cnt= split(str, a, 100);
cout << "There are " << cnt << " values in this string" << endl;
for(int i=0; i<cnt; i++)
{
cout << a[i] << endl;
}
return 0;
}
string nextstring(string str, int start_index)
{
string ans;
if(str[start_index] == ',' || str[start_index] == '\0')
{
ans=" ";
}
else{
ans=str[start_index]+nextstring(str, start_index+1);
}
return ans;
}
int split(string str, string a[], int max_size)
{
int j=0;
int ans=0;
double k=0;
while(j<max_size)
{
a[j]= nextstring(str,k);
string check=a[j];
if(isalpha(check[0])!= 0)
{
ans++;
}
k=k+a[j].length();
j++;
}
return ans;
}
It seems that your problem is that while(j<max_size){...} leads to j being incremented up to max_size. The line a[j]= nextstring(str,k); is at some points reading values that are outside your string which is really bad!
Replacing while(j<max_size){...} by while(j<max_size && k<str.length()){...} seems to be enough to make your code work!
Apart from that:
k has no reason to be a double! It should be an int (or something similar).
Since you are already using string, you should also learn to use vector. split is better written as:
int split(string str, vector<string> &a, int max_size)
{
int ans=0;
int k=0;
while(k<str.length())
{
string next = nextstring(str,k);
if(isalpha(next[0])!= 0)
{
ans++;
a.append(next);
}
k += next.length();
}
return ans;
}
The problem in your approach is to identify the end of the string, as there is no null terminator in a c++ string. Consider to update nextstring() to look for the end of string in a different manner:
string nextstring(string str, int start_index)
{
...
if(start_index == str.size() || str[start_index] == ',' ) //<===
{
ans=" ";
}
...
}
online demo
Additional recommendation
Note that it is not very nice to return a blank string when in reality it should be empty to reflect its real value (e.g. ",,"). You have no choice because otherwise you would have no mean in the calling function, to determine that the end of string was reached. But the consequence is thar all your strings have a trailing blank.
When you call recursively the function adding char to build the return string, you risk to have a considerable overhead. You could consider avoiding this, by replacing the else part:
ans=str.substr(start_index, str.find(',', start_index+1)-start_index);
However, as you have no trailing blank anymore, you need to adapt split() so to adapt its way to count the total number of chars parsed:
k=k+a[j].length()+1; // +1 because there's no longer a trailing blank.
Online demo

C reading in from a vertical line character delimited file

new to C and trying to read in a text file, "stocks", delimited with vertical line '|' characters. Aim is to open the file which is has a string and float value on each line, in the format:
TOM|149.62
JIM|23.25
I have read other posts about commas, colon and tab delimited files, but the "scanset" suggestion applied here as a [^|] between the %s and %f doesn't seem to work. I seem to have managed to store the first character of the string at least now, but the float value saved is nonsense. This will later be writing to the arrays declared after the file but the basic case of displaying the string and float value for each line means I can continue on my own. Appreciate any help you can give me.
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
// TOM|149.62
// JIM|23.25
int main()
{
FILE *stocks;
char *stock_Tickers[100];
float stock_Prices[100];
if ((stocks = fopen("Stocks.txt", "r")) == NULL)
{
fprintf(stderr, "Error opening file.\n");
exit(1);
}
char tempchar;
float tempfloat;
for (int i = 0; i < sizeof (stock_Tickers); i++)
{
if (feof(stocks))
{
break;
}
fscanf(stocks, "%s[^|]%f\n",&tempchar,&tempfloat);
cout << tempchar << " " << tempfloat;
cin.get();
}
return 0;
}
Update: #Michel Billaud Apologies, but I have one last error here. Your method worked perfectly, but when I try it on this slight variant it starts printing rubbish on the last floating point and breaks on subsequent loops. Looked at the local variables and the last array just isn't updating. I think it has to do with the new line, as when I change the float for an integer it still doesn't work. Can you see what I might be doing wrong as it seems the same format to me? Format this time is Ryan|B|IBM|100|176.10. All files are read in after this...Thanks.
FILE *trades;
// Ryan|B|IBM|100|176.10
// Ryan|S|IBM|50|177.10
char trade_User[100][20];
char trade_Type[100];
char trade_Tickers[100][4];
int trade_Quantity[100];
float trade_Prices[100];
int trade_Count = 0;
if ((trades = fopen("Trades.txt","r")) == NULL)
{
fprintf(stderr, "Error opening file.\n");
exit(1);
}
for (int i = 0; i < sizeof(trade_Tickers); i++)
{
if (feof(trades))
{
break;
}
fscanf(trades, "%19[^|]|%1[^|]|%4[^|]|%d[^|]|%f\n",
&trade_User[i], &trade_Type[i], &trade_Tickers[i], &trade_Quantity[i], &trade_Prices[i]);
printf("%s %c %s %d %f\n",
trade_User[i], trade_Type[i], trade_Tickers[i], trade_Quantity[i], trade_Prices[i]);
trade_Count++;
}
Hint: when trying to use a problematic feature, do it in a small separate program. So you won't take the risk to draw wrong conclusions because of another unrelated issue.
// just playing with |-separated fields
#include <stdio.h>
int main(int argc, char **argv)
{
char line[]="TOM|1234.56";
char name[20];
float value;
sscanf(line, "%[^|]|%f", name, &value);
printf("name = %s, value = %f\n", name, value);
return 0;
}
By the way: seems you're using the C++ compiler (because of "using namespace std").

fill Arry function not storing characters and outputting symbols

I wrote this program for class. I have a problem with displaying what in the arry
all i get is symbols instead of a character even though i filled them with char from a file provided by the professor. please help me figure out what is the problem here i need it for tomorrow
// This program grades multiple choice exams1. Each exam consists of 20 questions.
//Each question has one of four possible answers: A, B, C, or D.
#include <stdio.h>
#include<stdlib.h>
#include <math.h>
const int maxsize = 20;
int fillArry (FILE *fi,char arry[],const int maxsize);
double gradeExam (char arry[], char studarry[],const int maxsize);
void desplayResults (double right);
int main ()
{
char arry[maxsize];
char studarry[maxsize];
int num,num1,i,right;
FILE *fi,*sd;
fi = fopen ("CorrectAnswers.txt","r");
if ( fi == NULL )
{
printf( "Error opening input file.\n\n" );
exit(1);
}
sd = fopen ("StudentAnswers.txt", "r");
if ( sd == NULL )
{
printf( "Error opening input file.\n\n" );
exit(1);
}
num = fillArry (fi,arry,maxsize);
num1 = fillArry (sd,studarry,maxsize);
for (i=0;i<maxsize;i++)
{
printf("%c\n",arry);
}
for (i=0;i<maxsize;i++)
{
printf("%c\n",studarry);
}
right = gradeExam (arry, studarry ,maxsize);
printf("you got %i right\n", right);
desplayResults (right);
return 0;
}
int fillArry (FILE *fi,char arry[],const int maxsize)
{
int u = 0;
while((fscanf(fi,"%c",&arry)==1 )&& u< maxsize)
{
u++;
}
return u;
}
double gradeExam (char arry[], char studarry[],const int maxsize)
{
int i, right=0, wrong=0;
for (i=1;i<=maxsize;i++)
{
if (arry[i]==studarry[i])
right++;
else
{
printf ("question number %i :%c is wrong, the correct answer is %c\n\n",i,studarry,arry);
wrong++;
}
}
printf("\nnumber of wrong answers: %i\n",wrong);
return right;
}
void desplayResults (double right)
{
double res;
res = (right/20)*100;
printf("You got %s %.2lf on the exam\n","%",res);
if (res<70)
printf ("You failed the exam\n");
else
printf ("You passed the exam\n");
}
The problem is with your fscanf statement. Try this out.
fscanf(fi,"%s",arry)
Also, for displaying the array contents, you have to do it like this :
for(i=0; i<maxsize; i++)
{
printf("%c",arry[i]);
}
EDIT1 : I have verified the same thing at my end and it is working. Please check the contents of the file CorrectAnswers.txt.
EDIT2 : I got the problem. It is in your print statement :
printf ("question number %i :%c is wrong, the correct answer is %c\n\n",i,studarry,arry);
Please correct it to :
printf ("question number %i :%c is wrong, the correct answer is %c\n\n",i,studarry[i],arry[i]);