C++: Accessing a structure array's element crashes the program - c++

I'm trying to learn structures and I have a program which crashes for me - can't understand why. The compiler throws out a bad access exception when trying to access an element of an array of structures.
Here's the code:
#include <iostream>
#include <fstream>
FILE *input;
int n=1;
struct Student {
string name;
string city;
int grades[4];
};
Student *students = new Student[n];
int main(int argc, const char * argv[]) {
input = fopen("input.txt", "r");
if(input==NULL) {
cout<<"\nCan't open file input.txt";
return;
}
int size;
if(fscanf(input, "Size%d\n\n",&size)<=0) {
cout<<"\nAn error occurred.";
return;
}
n=size;
cout<<"\nThe size is "<<n;
for(int i=0;i<n-1;i++) {
Student newStud;
char name[255];
char city[255];
fscanf(input, "\n%s\n%s\n%d;%d;%d;%d;",name,city,&newStud.grades[0],&newStud.grades[1],&newStud.grades[2],&newStud.grades[3]);
newStud.fullName = name;
newStud.city = city;
cout<<"\nAdding student at "<<i;
students[i]=newStud;
cout<<"\nAdded successfully";
}
fclose(input);
cout<<"\nLoad complete!";
}
input.txt:
Size5
Bob
NY
10;10;10;10;
Rick
SF
6;6;6;6;
John
NY
2;3;2;5;
Jack
NY
5;5;5;4;
Console output:
The size is 5
Adding student at 0
Added successfully
Adding student at 1

The initialization of the students pointer is done before the execution of main. You wouldn't have read n by then. Therefore, your code ends up allocating memory for 1 Student in students array.
The code in your main assumes that the students array is capable of holding size (or the current value of n) while it isn't really the case. Hence, the code ends up accessing unknown locations which causes undefined behavior (a segmentation fault almost always).
The fix is to allocate memory for the array after the input for n has been taken.

Obviously,the program crashed when you tried to access the second Student in the array while you only allocated one size of the Student for it. You should allocate the array’s memory after the code “n = size” but not before,which you did at the top of the main entrance is absolutely unreasonable for your purpose.

Related

What is the problem I am having with using arrays with classes?

I have been working on a project for my computer science class and have encountered an issue with the code working. I am shown no error except when I try to compile and I get an error that reads:
Exception thrown: write access violation.
_Left was 0xCCCCCCCC.
The purpose of my project is to take a list of names from an external file, read them into an array, sort said array and then output the sorted list all while using a class for the code.
Here is a copy of my code and I would like to extend my gratitude to whoever can help me through my issue:
**Header File**
#include <iostream>
using namespace std;
class person
{
public:
person();
bool get(ifstream&);
void put(ofstream&);
private:
int capacity = 0;
string first_name[CAPACITY];
string last_name[CAPACITY];
int age[CAPACITY];
};```
**Header function definitions cpp file**
#include<iostream>
#include<string>
#include<fstream>
#include<cstdlib>
const int CAPACITY=20;
using namespace std;
#include "Person.h"
//Names constructor
//Postcondition both first name and last name initialized to zero
person::person()
{
first_name[CAPACITY] = "";
last_name[CAPACITY] = "";
age[CAPACITY]=0;
}
bool person::get(ifstream& in)
{
in >> first_name[CAPACITY] >> last_name[CAPACITY] >> age[CAPACITY];
return(in.good());
}
void person::put(ofstream &out)
{
out << first_name[CAPACITY] << last_name[CAPACITY] << age[CAPACITY];
}
**cpp file which holds main**
#include<iostream>
#include<cstdlib>
#include<fstream>
#include<string>
const int CAPACITY = 20;
using namespace std;
#include "Person.h"
void pop(string *xp, string *yp);
void sort(string name[CAPACITY], int count);
int main()
{
class person names[CAPACITY];
ifstream infile;
ofstream outfile;
string filename;
string name[CAPACITY];
int n = 0;
cout << "Enter the file name you wish to open" << endl;
cin >> filename;
infile.open(filename + ".txt");
outfile.open("Person_New.txt");
if (infile.fail())
{
cout << "The file requested did not open" << endl;
exit(1);
}
while (!infile.eof())
{
names[n].get(infile);
n++;
}
sort(name, CAPACITY);
for (int i = 0; i < CAPACITY; i++)
{
names[i].put(outfile);
}
cout << "The file has been created" << endl;
infile.close();
}
void pop(string *xp, string *yp)
{
string temp = *xp;
*xp = *yp;
*yp = temp;
}
void sort(string name[CAPACITY], int count)
{
int i, j;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (name[j] > name[j + 1])
{
pop(&name[j], &name[j + 1]);
}
}
}
}
Once again Thank you for any support
It sounds to me like the compiler is getting upset that you are trying to write (i.e. assign a value) at an address that you do not have permission to access. I believe your constructor for the class person might be at fault because of how this class stores its variables, as well as the class header:
Constructor for the class person:
`person::person(){
first_name[CAPACITY] = "";
last_name[CAPACITY] = "";
age[CAPACITY] = 0;
}`
Class header for the class person:
`class person{
public:
//stuff
private:
int capacity = 0;
std::string first_name[CAPACITY];
std::string last_name[CAPACITY];
int age[CAPACITY];
//more stuff
}`
C++ is very specific about its naming conventions, so it makes a distinction between capacity and CAPACITY. Because of this, the variable CAPACITY is not defined within the Person.h file.
Also, because CAPACITY is set to a fixed value in your Person.cpp file, whenever you use first_name[CAPACITY], last_name[CAPACITY], or age[CAPACITY] to assign new values, you are only updating the values at the index equal to CAPACITY unless you update the value of CAPACITY itself. In the code you provided, CAPACITY is equal to 20, so your program attempts to update exclusively index 20 with each method call. This will likely cause issues since the person class only attempts to make its arrays on the runtime stack, with a size of 0 each.
Separately, it seems like you want an array of people, but it appears that you are attempting to use a single person object to store the names and ages of multiple people by making these all arrays. Instead, I would recommend making first_name, last_name, and age not arrays, but rather single variables. Then, you can manipulate an array of type person using your CAPACITY variable. You got pretty close, but you can instead declare it as person myPersonArray[CAPACITY] (no need to mention "class" in front of it -- just be sure that you have #include "Person.h" in your main.cpp file). When you want to update a specific person, you can perform an operation like myPersonArray[updateThisIndexNum].update(newFirstName, newLastName, newAge) or some logical equivalent.
As a final note, I almost always highly recommend against using !infile.eof() to control your while loop when reading any file because eof() only indicates whether you have tried to read past the end of an input file. I would highly recommend checking out this post on Stack Overflow where people far more knowledgeable than I explain exactly why this is usually dangerous and how to avoid it.

Adding element to array of struct c++

Can someone explain why this code does not work? It keeps crashing when it asks for input in addCar().
I think something is wrong with copying an array, but I can't figure out what exactly. I also tried to use copy() but it didn't work either.
#include <iostream>
#include <string>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct Car{
string Brand;
string model;
long mileage;
};
void addCar(int *ptr, struct Car *arra){
*ptr=*ptr+1;
Car *newArr = new Car[*ptr];
memcpy(newArr, arra, (*ptr)*sizeof(Car));
cout<<"Brand ";
getline(cin,newArr[*ptr].Brand);
cout<<"Model ";
getline(cin, newArr[*ptr].model);
cout<<"mileage ";
cin>>newArr[*ptr].mileage;
arra=newArr;
};
int main(int argc, char** argv) {
int size=1;
int *ptr_size;
ptr_size=&size;
Car *tab=new Car[*ptr_size];
tab[0].Brand = "Audi";
tab[0].model = "A8";
tab[0].mileage = 14366;
addCar(*ptr_size, tab);
return 0;
}
The fail is probably here:
getline(cin,newArr[*ptr].Brand);
A bit above, you did this: *ptr=*ptr+1; and made newArr an array of *ptr elements. Arrays are origin zero. That means the first item in the array is newArr[0]. The last will be at newArr[*ptr-1], so writing into newArr[*ptr] is writing over someone else's memory. Generally a bad thing to do.
But this is also not cool:
*ptr=*ptr+1;
Car *newArr = new Car[size+1];
memcpy(newArr, arra, (*ptr)*sizeof(Car));
You increment the size of the array. That's OK.
You create a new array with the new size. That's OK.
You copy new size number of elements from the old array to the new array and over shoot the end of the old array. Not OK.
The best answer is given by Jerry Coffin and Paul McKenzie in the comments: use a std::vector. If this is not allowed... Ick.
But alrighty then.
First, memcpy literally copies a block of memory. It does not know or care what that block of memory is or what it contains. Never use memcpy unless you are copying something really, really simple like basic data type or a structure made up of nothing but basic data types. String is not basic. The data represented by a string might not be inside the string. In that case, you copy a pointer to the string and that pointer will not be valid after the death of the string. That's not a problem in your case because you don't kill the string. That leads to problem 2. Let's fix that before you get there. The easiest way (other than vector) is going to be:
for (int index = 0; index < *ptr-1; index++)
{
newArr[index] = arra[index];
}
An optimization note. You don't want to resize and copy the array every time you add to it. Consider having two integers, one size of array and the other index into array and double the size of the array every time the index is about to catch up with the size.
When you allocate any memory for data with new somebody has to clean up and put that memory back with delete. In C++ that somebody is you. so, before you arra=newArr; you need to delete[] arra;
Passing in the array index as a pointer overcomplicates. Use a reference or just pass by value and return the new index. Also, don't name a variable ptr. Use something descriptive.
void addCar(int &arrasize, struct Car *arra){
or
int addCar(int arrasize, struct Car *arra){
Next problem: int addCar(int arrasize, struct Car *arra){ passes in a pointer to arra. But you passed the pointer by value, made a copy of the pointer, so when you change the pointer inside the function, it's only the copy that got changed and the new array is not going to come back out again. So,
int addCar(int arrasize, struct Car * & arra){
Passes in a reference to the pointer and allows you to modify the pointer inside the function.
Putting all that together:
int addCar(int size, struct Car * & arra)
{
Car *newArr = new Car[size + 1];
for (int index = 0; index < size; index++)
{
newArr[index] = arra[index];
}
cout << "Brand ";
getline(cin, newArr[size].Brand);
cout << "Model ";
getline(cin, newArr[size].model);
cout << "mileage ";
cin >> newArr[size].mileage;
delete[] arra;
arra = newArr;
return size+1;
}
int main()
{
int size=1;
Car *tab=new Car[size];
tab[0].Brand = "Audi";
tab[0].model = "A8";
tab[0].mileage = 14366;
size = addCar(size, tab);
// do more stuff;
// bit of test code here
for (int index = 0; index < size; index++)
{
cout << "Car " << index << " brand =" <<tab[index].Brand << " Model=" << tab[index].model << " mileage=" <<tab[index].mileage << endl;
}
delete[] tab;
return 0;
}
When you are copying the old array to the new one you are accessing invalid memory, remember that, in that point, arra has size *ptr-1 not *ptr, so the line should be
memcpy(newArr, arra, (*ptr-1)*sizeof(Car));
also in the other lines you should insert the new value in the *ptr-1 position because the indexes in newArr go from 0 to size-1 ie *ptr-1:
cout<<"Brand ";
getline(cin,newArr[*ptr-1].Brand);
cout<<"Model ";
getline(cin, newArr[*ptr-1].model);
cout<<"mileage ";
cin>>newArr[*ptr-1].mileage;

C++ struct pointer and memory leak

I am doing a c++ book question and got stucked on this specific parts where it says
"write program that asks how many score there are and how many student there are. it should then dynamically allocate an array of structures, each structure's Test member should point to a dynamically allocated array that will hold the test scores,after array has been dynamically allocated the program should ask for the id number and test score for each student".
right now I have a problem in the for loop where there is a memory leak and program crashes after input values, any suggestions ?
here is the code:
struct Course
{
string Name;
int IdNumber;
int *Tests;
int Average;
int courseGrade;
};
void Result(int,int );
int main()
{
cout<<"please enter number of test score ";
int testScore;
cin>>testScore;
int numberofStudents;
cout<<"please enter number of student there is ";
cin>>numberofStudents;
Result(numberofStudents,testScore);
}
void Result(int numberofStudents,int testScore)
{
const int Size=numberofStudents;
const int Size1=testScore;
Course *object=nullptr;
object=new Course[numberofStudents];
object->Tests = new int[testScore];
for(int i=0;i<testScore;i++)
{
cin>>object[i].Tests[i];
}
}
here is the input on the console
please enter number of the test scores :3
please enter number of students there is :3
34
90
the program crashes after I input 90
Here's what I suspect is happening:
In this for loop:
for(int i=0;i<testScore;i++)
{
cin>>object[i].Tests[i];
}
You access object using testScore as an index. If testScore is larger than the length of object you will run into issues.
The memory leak problem comes from the fact that you are allocating space for object and every Tests member of a Course but you never free that memory.
There is a memory leak because you are allocating things withj new and never freeing them with delete.
As for the crash, it's being caused by this line:
object->Tests = new int[testScore];
Remember that object isn't a Course object, it's an array of Course objects, every one of which needs it's own Tests array. The line is effectively just object[0].Tests = ...
So You need a loop on numberofStudentsto allocate the test in each Course, and the another loop on numberofStudents around the loop on testScore (which you already have) to gather all the grades. (Advanced study: you can combine those two loops)
I have made some changes in your code, and it doesn't crash anymore. Why you need to use pointer of Test. I think it is good to use static memory instead of dynamic memory. Please check this
#include<iostream>
#include<cstring>
using namespace std;
struct Course
{
string Name;
int IdNumber;
int Tests[100];
int Average;
int courseGrade;
};
void Result(int,int );
int main()
{
cout<<"please enter number of test score ";
int testScore;
cin>>testScore;
int numberofStudents;
cout<<"please enter number of student there is ";
cin>>numberofStudents;
Result(numberofStudents,testScore);
}
void Result(int numberofStudents,int testScore)
{
const int Size=numberofStudents;
const int Size1=testScore;
Course *object=nullptr;
object=new Course[numberofStudents];
//object->Tests = new int[testScore];
for(int i=0;i<testScore;i++)
{
cin>>object[i].Tests[i];
}
}

Read File then Create Array Function C++

I am new to C++ and lately I was having trouble reading a file. That problem is now fixed. What I'd like to do now is to use the file I read to create an array, but all this has to be done in a function, this means the function has to return an array so I can use it on the main. The process I follow:
Read File
Create Array
Return Array Adress.
In the main: Use function to put an array in a variable.
My code so far:
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
using namespace std;
void imprimeArreglo (int[],int);
int* leeArreglo(string&);
int i;
void imprimeArreglo(int a[])
{
cout << a[i] << endl;
}
int* leeArreglo(string nombre)
{
ifstream arch(nombre.c_str());
string tamStr;
int tam;
if(arch.is_open())
{
getline(arch,tamStr);
tam=atoi(tamStr.c_str());
int arr[tam];
for(int i=0;i<tam;i++)
{
arch >> arr[i];
}
int *ret=arr;
return ret;
}
return 0;
}
int main()
{
string nombre = "arr.txt";
int *a= leeArreglo(nombre.c_str());
imprimeArreglo(a);
return 0;
}
What my arr.txt file looks like:
10
9 8 22 33 76 0 23 100 -8 45
I think this should print (with imprimeArreglo(a)):
9 8 22 33 76 0 23 100 -8 45
But the only thing I get in the console is:
2292592 Which I think it's a memory adress??? What am I doing wrong? Is there a better way to do it without using vectors?
Thanks in advance!
This would be a simple way of doing it:
#include <iterator>
#include <vector>
#include <fstream>
std::vector<int> fileContents(const std::string& filename)
{
ifstream inputFile(filename.c_ctr());
std::istream_iterator<int> end;
std::istream_iterator<int> it(inputFile);
return std::vector<int>(it, end);
}
This reads all the numbers into a vector. You may have to modify it to ignore the first 10, for example by calling std::advance(it, 1); just after initializing it.
You are returning a reference to a local variable. When your function go out of scope the array arr is freed and therefore the address return is just garbage.
If you want to return an address that lives after the function is called, do something like:
int *arr = new int[tam];
Then the address will be valid after the function is called but in terms of design this is not great as you leave the responsibility to free the array on the caller.
I would personally wrap all of this in a class and and have the array member of it.
Ok after some research and consulting, I got the answer! Thank you everyone for helping me :). This is the code that made the things happens (USING ARRAYS!!)
int* readArray(string name)
{
ifstream fil(name.c_str());
string sizeStr;
int size;
if(fil.is_open())
{
getline(fil,sizeStr);
size=atoi(sizeStr.c_str());
int* arr = new int[size+1];
for(int i=1;i<size+1;i++)
{
fil >> arr[i];
}
//We stash the size in the first position ;)
arr[0]=size;
//cout << arr[0]<< "\n";
//We point to the position of the array + 1. (The 0 pos is used for the size)
int *ret=arr+1;
return ret;
delete[] ret;
}
else cout << "ERROR...!\n";
return 0;
}
As you can see my function returns a pointer. And I delete it. (Still need to see if that delete[] is well positioned tho.)
Also since what I return is a pointer I can't really know what the size of my array is using sizeof(arr)/sizeof(int). So what I did is to save it into a position of the array, that way I can call it whenever I want by using array[-1].
Thank you all again!

glibc detected : double free or corruption

I will explain the brief coding steps I have done and area where I am facing the problem
main.cpp
int main()
{
int cnt_map,i=1,value;
/* My question is about this char pointer "key" */
char *key =(char*)malloc(sizeof(char) * 25);
if(key!=NULL)
{
printf("Key value is not NULL,its value is:%x\n",key) ;
cout<< "Enter the number of elements required in container map"<<endl;
cin >> cnt_map;
for (i=1;i<=cnt_map;i++)
{
cout << "Enter the key : ";
cin >>key;
cout << "Enter the key value:" ;
cin >>value;
printf("value pointed by ptr key: %s, value in ptr: %x\n", key,key);
c -> add_map1(key,value); //Function inserts value to map container
key+=sizeof(key);
}
c -> size_map1(); //Function displays size of map container
c -> display_map1(); //Function displays contents of map container
if(key)
{
printf("FINALLY:value pointed by ptr key: %s, value in ptr: %x,size:%d\n",key, key, sizeof(key));
free(key);
}
}
return 0;
}
when tried compiling and running the above code, I am able to successfully compile the code but got "glibc detected : double free or corruption" when tried running the application.
Now my question is I created a char pointer(char *key =(char*)malloc(sizeof(char) * 25);)
and successfully assigned memory to it using malloc. After completing my process when I tried freeing of that char pointer I am getting double free or corruption error. I learned that any variable assigned memory with malloc/calloc should be freed finally. Please tell why I am this getting error, why I should not do this? Please tell me how the memory operations are ongoing on char* key (if possible pictorially).
Note: The code presented above is not the complete code, I just explained where I am getting the problem and if I am not freeing the pointer variable, my application is running successfully.
By doing this:
key+=sizeof(key);
your key variable is no longer pointing to the start of the memory you allocated. You must pass the original pointer to free(). You need to store the original pointer in another variable so that you can correctly free() it at the end.
(You may be able to simply remove that line - I'm not sure what it's doing, given that sizeof(key) is either 4 or 8. I suspect it's redundant.)
That's because of this line : key+=sizeof(key); . key doen't contain the same address as the malloc returned address.
For example:
char *key =(char*)malloc(sizeof(char) * 25);
Let's say malloc returns the address 20000 (totally dumb address, it's just for the example).
Now you're doing key+=sizeof(key);, so key = 20000 + 4 = 20004. The problem is you're trying to free key, which points to the address 20004 instead of 20000.
In order to fix that, try this:
int main()
{
int cnt_map,i=1,value;
char *key_save;
/* My question is about this char pointer "key" */
char *key =(char*)malloc(sizeof(char) * 25);
key_save = key;
if(key!=NULL)
{
printf("Key value is not NULL,its value is:%x\n",key) ;
cout<< "Enter the number of elements required in container map"<<endl;
cin >> cnt_map;
for (i=1;i<=cnt_map;i++)
{
cout << "Enter the key : ";
cin >>key;
cout << "Enter the key value:" ;
cin >>value;
printf("value pointed by ptr key: %s, value in ptr: %x\n", key,key);
c -> add_map1(key,value); //Function inserts value to map container
key+=sizeof(key);
}
c -> size_map1(); //Function displays size of map container
c -> display_map1(); //Function displays contents of map container
if(key)
{
printf("FINALLY:value pointed by ptr key: %s, value in ptr: %x,size:%d\n",key, key, sizeof(key));
free(key_save);
}
}
return 0;
}
Simply remove the line:
key+=sizeof(key);
key is not a pointer to an array of strings, it's a pointer to a single string. Every time you increment this, you're reducing the available space in the string. The first time you read a key, there's 25 bytes available. The next time, you've incremented key by 4 or 8 bytes, but the end of the allocated space hasn't changed, so now there's only 21 or 17 bytes available. The third time it's only 17 or 9 bytes, and so on. After a few iterations, you'll increment key past the end of the memory block that you allocated, and it will start writing into unallocated memory (or memory that's assigned to other data structures). This is undefined behavior, and will most likely cause unpredictable failures in your program.
Since you're using C++, you should use std::string instead of char[] for strings, and std::vector instead of ordinary arrays. These data structures automatically expand as needed, so you avoid buffer overflows like this.
this is not taking your code into consideration, but i had a same problem in Reader writer problem (operating systems) http://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem.
It was due to file pointer being global so whenever any reader tried to read and in b/w another read reads and closes file pointer so when another reader which has not finished reading tried to close file pointer after reading. so what happened is file pointer was already closed it wasn't pointing to any file.
Solution i used.
Instead of declaring file pointer global i declared it local to reader function that it or else you can check file pointer for NULL and if NULL then don't close the file pointer.
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
sem_t x,wsem;
int rc=0;
char ch;
char str[20];
void *reader(void *);
void *writer(void *);
int main()
{
int nw,nr,i=0,j=0;
pthread_t w[10],r[10];
sem_init(&x,0,1);
sem_init(&wsem,0,1);
rc=0;
printf("Enter the no of readers:");
scanf("%d",&nr);
printf("Enter the no of writers");
scanf("%d",&nw);
while(i<nw || j<nr)
{
if(i<nw)
{
pthread_create(&w[i],NULL,writer,(void *)i);
i++;
}
if(j<nr)
{
pthread_create(&r[j],NULL,reader,(void *)j);
j++;
}
}
for(i=0;i<nw;i++)
{
pthread_join(w[i],NULL);
}
for(j=0;j<nr;j++)
{
pthread_join(r[j],NULL);
}
return 0;
}
void *reader(void *arg)
{
FILE *fptr;
sem_wait(&x);
rc++;
if(rc==1)
sem_wait(&wsem);
sem_post(&x);
printf("\nreader %d:",arg);
fptr=fopen("temp.txt","r+");
while(fgets(str,10,fptr)!=NULL)
{
printf("%s",str);
}
printf("\n");
fclose(fptr);
sem_wait(&x);
rc--;
if(rc==0)
sem_post(&wsem);
sem_post(&x);
}
void *writer(void *arg)
{
FILE *fptr1;
sem_wait(&wsem);
printf("\nwriter-%d:\n",arg);
fptr1=fopen("temp.txt","a+");
printf("enter the string:");
scanf("%s",str);
fputs(str,fptr1);
fclose(fptr1);
sem_post(&wsem);
}