Unhandled exception

hey guys i am getting

"Unhandled exception at 0x003C96BB in LAB4TEST.exe: 0xC0000005: Access violation reading location 0x01E86F90"

i'm not sure where to start on this one. I know from reading the error it's having issues trying to access information from the file when it can not. How should i go about tackling it? it breaks right at Ships[shipCount]->name = new char[strlen(tempString) + 1]; in lab4class.cpp line 38

lab4class.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "lab4Header.h" 
#include <iostream>
#include <fstream>
#include <iomanip>

//initialize pointer
starFleet::starFleet()
{
	for (int i = 0; i < FLEET_SIZE; i++)
	{
		Ships[i] = NULL; // RM, changed from ships to Ships, as per your class.
	}
	shipCount = 0;
}
starFleet::~starFleet()
{
	for (int i = 0; i < shipCount; i++)
	{
		delete[] Ships[i]->name;
		delete[] Ships[i]->type;
		delete[] Ships[i]->position;
		delete[] Ships[i]->condition;
		delete[] Ships[i]->captain;
		delete[] Ships[i];
	}
}
void starFleet::loadData()
{
	shipCount = 0; //set the count = 0
	ifstream inputFile;
	cout << "Loading Data file......." << endl;
	inputFile.open("starships.txt");
    char tempString[S_SIZE];  // RM, temporary holder for input info.
	while (!inputFile.eof())
	{
        Ships[shipCount] = new shipData;  // RM, Set aside data for a new ship.
        inputFile.get(tempString, S_SIZE, ';');
        Ships[shipCount]->name = new char[strlen(tempString) + 1]; // RM, Set up the data for name, just the right size.
		inputFile.get(tempString, S_SIZE, ';');
		strcpy(Ships[shipCount]->name, tempString);  // RM, Copy the name info into the name array.
		inputFile >> Ships[shipCount]->regNum;
		inputFile.get(tempString, S_SIZE, ';');
		Ships[shipCount]->type = new char[strlen(tempString) + 1]; // RM, Set up the data for name, just the right size.
		strcpy(Ships[shipCount]->type, tempString);
		inputFile.get(tempString, S_SIZE, ';');
		Ships[shipCount]->position = new char[strlen(tempString) + 1]; // RM, Set up the data for name, just the right size.
		strcpy(Ships[shipCount]->position, tempString);
		inputFile.get(tempString, S_SIZE, ';');
		Ships[shipCount]->condition = new char[strlen(tempString) + 1]; // RM, Set up the data for name, just the right size.
		strcpy(Ships[shipCount]->condition, tempString);
		inputFile.get(tempString, S_SIZE, ';');
		Ships[shipCount]->captain = new char[strlen(tempString) + 1]; // RM, Set up the data for name, just the right size.
		strcpy(Ships[shipCount]->captain, tempString);
        /* RM, modify all of the code for type, position, etc. so it matches the code for name. You can reuse tempString for this.
          You need to use tempString first, set aside the memory with new, and then copy the data over using strcpy.*/
		shipCount++; 
	}
	if (shipCount > 0)
		shipCount--;
	inputFile.close();
	std::cout << endl << "Successfully loaded " << shipCount << " Ships." << endl << endl; // return the total count 
	return;
}


void starFleet::addstarship()
{
	Ships[shipCount] = new shipData();  // RM, your struct is called shipData, not Ship.
    fstream file("starships.txt", fstream::app);
	
		char tempName[S_SIZE];
		cout << " What is the name of the ship ? : ";
		cin.get(tempName, S_SIZE);
		Ships[shipCount]->name = new char[strlen(tempName) + 1]; // + 1 for null.
		strcpy(Ships[shipCount]->name, tempName);
	
		cout << " What is the registry number of the ship ? : ";
		cin >> Ships[shipCount]->regNum;
	
		cout << " What is the type of the ship ? : ";
		cin.get(tempName, S_SIZE);
		Ships[shipCount]->type = new char[strlen(tempName) + 1]; // + 1 for null.
		strcpy(Ships[shipCount]->type, tempName);
	
		cout << " What is the position of the ship ? : ";
		cin.get(tempName, S_SIZE);
		Ships[shipCount]->position = new char[strlen(tempName) + 1]; // + 1 for null.
		strcpy(Ships[shipCount]->position, tempName);
	
		cout << " What is the condition of the ship ? : ";
		cin.get(tempName, S_SIZE);
		Ships[shipCount]->condition = new char[strlen(tempName) + 1]; // + 1 for null.
		strcpy(Ships[shipCount]->condition, tempName);
	
		cout << " What is the captain of the ship ? : ";
		cin.get(tempName, S_SIZE);
		Ships[shipCount]->captain = new char[strlen(tempName) + 1]; // + 1 for null.
		strcpy(Ships[shipCount]->captain, tempName);

        // RM, can't use dots with pointers. You have to dereference with (->).
		file << '\n' << Ships[shipCount]->name << ';' << Ships[shipCount]->regNum << ';' <<
			Ships[shipCount]->type << ';' << Ships[shipCount]->position << ';' <<
			Ships[shipCount]->condition << ';' << Ships[shipCount]->captain;
		shipCount++;
	return;


}
void starFleet::displayFleet()
{
	// int count = 0; RM, not necessary.
	if (shipCount > 0)
	{
		cout << "List all of the fleet!" << endl;
		for (int i = NULL; i < shipCount; i++)
		{
            // RM, can't use dots with pointers. Have to dereference with (->).
			cout << "Name of the ship: " << Ships[i]->name << endl;
			cout << "Registry number: " << Ships[i]->regNum << endl;
			cout << "Type of ship" << Ships[i]->type << endl;
			cout << "Position of ship: " << Ships[i]->position << endl;
			cout << "Condition of the ship: " << Ships[i]->condition << endl;
			cout << "Captain of the ship: " << Ships[i]->captain << endl;
		}
	}
}
bool starFleet::searchFleet()
{
	int regNum1;
	cout << "Please enter the registry number of the fleet you would like to search for: ";
	cin >> regNum1;

	for (int d = NULL; d < shipCount; d++)
	{
        // RM, can't use dots with pointers. Use -> to dereference.
		if (Ships[d]->regNum == regNum1){
			cout << setprecision(2) << setw(5) << left << "Name: " << Ships[d]->name << endl;
			cout << setprecision(2) << setw(5) << left << "registry number: " << Ships[d]->regNum << endl;
			cout << setprecision(2) << setw(5) << left << "type: " << Ships[d]->type << endl;
			cout << setprecision(2) << setw(5) << left << "Position: " << Ships[d]->position << endl;
			cout << setprecision(2) << setw(5) << left << "Condition of ship: " << Ships[d]->condition << endl;
			cout << setprecision(2) << setw(5) << left << "Captain of ship: " << Ships[d]->captain << endl;
			return true;
		}
	}
	cout << "That fleet does not exsist" << endl;
	return false;
}

// RM, getCount implementation.
int starFleet::getCount()
{
    return shipCount;
}

lab4header.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#ifndef LAB4CLASS_H
#define LAB4CLASS_H

const int S_SIZE = 256;
const int FLEET_SIZE = 30;
using namespace std;

struct shipData
{
	char* name;
	int regNum;
	char* type;
	char* position;
	char* condition;
	char* captain;
};

class starFleet
{
public:
	void addstarship(); // RM, changed name from addstarships to addstarship.
	void displayFleet();
	void loadData();    // RM, changed to void from int.
	bool searchFleet();
	int getCount();
	starFleet(); // Constructor
	~starFleet(); //deconstructor
	// Other methods go here.

private:
	shipData * Ships[FLEET_SIZE]; // RM, added the star, for array or pointers and changed name to Ships.
	int index;
	int shipCount;
};
#endif 
[/code]
lab4main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstring>
#include "lab4Header.h" 

using namespace std;
int main()
{
	starFleet myFleet;
	ofstream outputFile;
	ifstream inputFile;
	int count = 0;
	char choice;
	myFleet.loadData();
	inputFile.close();

	fstream file("starships.txt", fstream::app);
	do {
		cout << "\n**********************************" << endl;
		cout << "Welcome to ****'s star fleet manager" << endl;
		cout << "Here are your following options." << endl;
		cout << "a. Add a fleet" << endl;
		cout << "s. search for a fleet" << endl;				 /*Main Menu*/
		cout << "l. list all fleets" << endl;
		cout << "q. To quit the fleet manager." << endl;
		cout << "Please use all lower case during the program" << endl;
		cout << "**********************************" << endl;
		cout << "please enter your choice: ";
		cin >> choice;
		cin.ignore(); // Added ignore, to get rid of '\n' stuck in the buffer.

		switch (choice) {
		case 'a':
		case 'A':
			if (myFleet.getCount() < FLEET_SIZE)
			{

				myFleet.addstarship();
				break;
			}
			else
				cout << "Sorry, no more room left in the system.";
			break;
		case 's':
		case 'S':
			if (myFleet.getCount() > 0)
			{
				myFleet.searchFleet();
				//class call bool search fleet
				break;
			}
			else
				cout << "sorry no Systems have been added in the database yet" << endl;
			break;
		case 'l':
		case 'L':
			myFleet.displayFleet();
			break;
		case 'q':
		case 'Q':
			cout << "Thank you for using ****'s Star Fleet manager." << endl;
			cout << "Endoy the rest of your day." << endl;
			file.close();
			system("pause");
			return 0;
			break;
		}
	} while (choice != 'q');

	return 0;
}

Bump anyone?
That is a ridiculous amount of manual memory management. std::string/std::vector would be better choices. I can't see any reason for Ships to be an array of pointers. An array of ShipData would be less prone to error.

There should be a constraint that ShipCount is less than or equal to FLEET_SIZE. This isn't enforced anywhere.

Do not loop on eof. If you do you'll end with hacks like lines 58/59 in starFleet::loadData which causes a memory leak.

My guess is that the bolded line above is the problem that is exacerbated by looping incorrectly on eof. Are there exactly FLEET_SIZE ships you're trying to store?
oh whoops I forget to add the data file.

Voyager;74656;Intrepid;Delta_Quadrant;Unknown;Janeway
Grissom;638;Oberth;Genisis_Planet;Destroyed;Esteban
Enterprise;1701;Constitution;Klingon_Frontier;Operational;Kirk

I would like to use string and vector, but I can not use strings is this program cause well the instructor said.

my global for FLEET_SIZE is in the header file.
const int FLEET_SIZE = 30;

Then If i shouldn't loop on EOF then what should i do? All i have been taught this term is that i can use EOF like that and it hasn't given me a problem since. I have tried working around somethings on it with no avail.

Is there any other ideas?

Here is what I am suppose to do with this code.

CS 162 Computer Science II
Programming Assignment Lab 4

An alien digital life form has taken up residence in the Enterprise data banks. This is a first contact opportunity, and the xenomorphology lab needs as much memory space as possible to make a hospitable environment for the digital aliens. Therefore the captain has mandated that all programs will only be allocated a minimum amount of memory. So you will re-write the code for assignment 3 to use dynamic memory whenever possible. Therefore, this lab assignment will have the same requirements as lab 3, with the added requirement that all arrays will be dynamic, and will use the minimum amount of memory.

Strategies for using the minimum amount of memory
Lab 3 required you to use a class for Starfleet ships, using an internal array of structs or classes. The Starfleet class and Ship struct (or class) might look something like the textbox to the right. Suppose you have a constant called FLEET_SIZE that you use to create your fleet array, and S_SIZE for your c-strings (char arrays).
Instead of creating an array of ships with structs in memory that may or may not be used, you will create an array of pointers for Ships. Then you will only allocate memory whenever a Ship is read from the file or entered by the user. And instead of c-string arrays that are a fixed (and long) size, you will wait until run time to find out how much memory each c-string needs. Then you will allocate exactly enough memory for the c-string, and no more. So for assignment 4, your class and struct arrangement might look something like the textbox to the left and below.
Now that you will be using Ship pointers for the Ships array, you will need a constructor for the class that will set every Ship pointer in the array to the nullptr. You can use a loop for this. Then you will have to allocate a new ship whenever you want to load the data for a ship into memory. For example, suppose the user wants to enter a new ship from the keyboard, so the method addShip() is called. One of the first things to do inside of addShip() is to set aside memory for the new ship’s data:
Ships[shipCount] = new Ship;
Now that you have a struct set aside, you will need to get and set the data (name, registry, type, etc.). Most of the members are char pointers that will point to c-strings. So to set aside just the right amount of space for a c-string, you can use a temporary c-string that is large enough to handle any name, say 128 or so, which is how we might set S_SIZE. Then we can use strlen() to find out how long the name is, and set aside that amount of memory (plus 1 for the null terminator). So suppose you want to set aside memory for the name of the ship. You could ask for the ship’s name with a temporary string:
char tempName[S_SIZE];
cout << “What is the name of the ship?: “;
cin.get(tempName, S_SIZE);
Now that you know what the name of the ship is, all you have to do is set aside the memory and copy over the name:

Ships[shipCount]->name = new char[strlen(tempName) + 1]; // + 1 for null.
strcpy(Ships[shipCount]->name, tempName);

When the user decides to quit the program (by selecting ‘q’ or ‘Q’ from the menu), you must deallocate all the dynamic memory that it used before program termination. Therefore you will need a destructor for the Starfleet class. You may also optionally create a destructor for the Ship struct. If you decide to only use a Starfleet destructor, you can delete allocated memory for each Ship item in the array:
for (int i = 0; i < shipCount; i++){
if (Ships[i] != nullptr){ // Make sure we don’t try to delete memory
// that hasn’t been allocated.
delete[] Ships[i]->name;
// Also delete the other dynamic c-strings (type, position, etc.)
}
}
Now that all of the c-strings have been deallocated, you can delete the memory set aside for the Ships array.
Please Note:
If your program has any memory leaks, you will loose points on this assignment. Therefore use valgrind on the Linux systems, or some other leak checking program or utility to make sure you deleted all dynamically allocated memory before program termination.

Design Considerations
Please follow the specifications below and do not deviate from them. Failure to follow the specifications will result in deduction of points.
1. Name your main file lab4main.cpp, and your additional files appropriately (lab4ClassImp.cpp and lab4Header.h for example). Please upload your data file as well.
2. Please be sure the source file includes your name, assignment description and number, and date, as a program comment. Also include a Sources line.
3. You MUST use a class to model the collection of starships (class Starfleet, for example).
4. Write methods for your Starfleet class to do the tasks (load the data, add a new ship, search for a ship, etc.). You may write any other methods that you think you need. You may create other nonmember functions, but if you do, they must not have any interaction with data items internal to a class.
5. Use a struct or a class named Ship, or something similar, to model the data for a starship.
6. You MUST use an array of Ship pointers to implement the above class (Starfleet). The array of structs must be internal to the class, and must be dynamically allocated.
7. You MUST use char pointers and dynamically allocated c-strings for all string-type items in a Ship struct (or class). You may optionally use a dynamically allocated c-string or an integer for a ship’s registration.
8. You must create a constructor for the Starfleet class that will set each Ship pointer in the array to the nullptr. Your constructor may optionally take care of other initialization needs, and you may optionally create other constructors with arguments if you wish.
9. You MUST use a destructor for the Starfleet class that will deallocate all dynamically allocated memory. You may optionally create a destructor for the Ship struct (or class).
10. When using a class, please make sure you encapsulate the data, which means make all the instance data members private and provide accessor methods and mutator methods to access and manipulate the data.
11. Make sure to have a delimiter written between each item in the file when you write the text file, such as a newline or a semicolon. This will be important when you read the information back from the file.

“Do-Not” List for All Labs in CS162:
• No Global Variables (you can have global constants)
• No use of the stdio library (use iostream and fstream)
• Instead of the string class, you will be using arrays of characters and the cstring library
• No goto statements.

Things You Should Do:
• Follow the style guide for this class
• Your programs should always guard against bad data being entered by mistake. Bad data means anything that could cause a stream to go into input failure.

Goals for This Lab:
• Using class to model Abstract Data Type
• OOP-Data Encapsulation
• Breaking tasks down into methods
• Dynamically allocated memory for arrays and c-strings.
• File input/output

How to Submit Your Work:
You may submit your files separately or zipped to the dropbox. Do not use .rar compression. You may upload as many versions as you wish prior to the due date. I will grade the last one unless you tell me otherwise.



There are a number of issues with the reading of data from starships.txt

As has already been said, don't loop on eof().

And specifically, the use of get like this:
 
inputFile.get(tempString, S_SIZE, ';');
reads up to the delimiting semicolon, which is left in the input buffer. Subsequent read operations will either fail or misbehave. Instead, use getline,
 
inputFile.getline(tempString, S_SIZE, ';'); 
this will read and discard the delimiter.

Similarly a formated input such as this will leave the semicolon in the buffer.
 
inputFile >> Ships[shipCount]->regNum;
You can remove it with ignore().

When reading the last item on each line, the delimiter is '\n', not ';'.

Last edited on
The problem with using a construction like this:
1
2
3
4
5
    while (!inputFile.eof())
    {
        Ships[shipCount] = new shipData;  // RM, Set aside data for a new ship.
        inputFile.get(tempString, S_SIZE, ';');
       // etc.  

... is that the eof() is tested before reading from the file.
But what is really important to know is the file status after attempting to read from the file.

Not only that, but the line Ships[shipCount] = new shipData; is executed regardless of whether or not the file access to follow will be successful.

One approach is to put the input operation as the condition of the loop, like this:
1
2
    while (inputFile.getline(tempString, S_SIZE, ';'))
    { 

This will at least try to verify that the input was successful before entering the loop - though there could still be problems if there are blank lines in the file.
Topic archived. No new replies allowed.