this is a part of an assignment for my programming class. the teacher wanted us to create a couple of functions, one of which would add elements to an existing dynamic array of structures, and this is what I have troubles with.
here's my understanding of how the function should work, based on different posts I found online:
create a new array, bigger than the one already existing
copy the content of the old array to the new array
add the new element to the new array
destroy the old array
however, something is wrong, and the program crashes - I think the problem lies in the way I'm trying to do points 3 and 4. Can someone take a look? I'd really appreciate any kind of help.
edit: forgot to mention, the teacher wants the functions set to void, they are supposed to not return anything.
Here is the code:
const int size = 2;
struct Player {
string name;
string kind;
};
void addplayer(Player * plarr, int size) {
cout << "Adding a new element to the array" << endl << endl;
//creating a new, bigger array:
Player * temp = NULL;
temp = new Player[size+1];
//copying the content of the old array
for (int i=0;i<size;i++) {
temp[i].name = plarr[i].name;
temp[i].kind = plarr[i].kind;
}
//adding the new element:
string name, kind;
cout << "Choose the name for the new player: " << endl;
cin >> name;
cout << "Choose the class for the new player: " << endl;
cin >> kind;
temp[size+1].name = name;
temp[size+1].kind = kind;
//deleting the old array, replacing it with the new one
delete[] plarr;
plarr = temp;
}
void addplayer(Player * plarr, int size) {
The plarr parameter is passed to this function by value.
This function appears to allocate a new array and copy over the contents correctly, except for one error:
temp[size+1].name = name;
temp[size+1].kind = kind;
The index should be size, here. But the biggest error is that the function concludes with:
delete[] plarr;
plarr = temp;
}
Unfortunately, since plarr was passed by value, all this does is set the plarr parameter to this function to the new pointer, and returns.
Which accomplishes absolutely nothing, since the caller to this function still has its original pointer. Which is now destroyed.
You should change this function to return the new pointer, which the caller will need to save, instead of the original pointer.
Related
I try to create objects dynamically. Each object is a pointer in an array of pointers. The compiler gives me
error C4700: uninitialized local variable 'villages' used.
And I can not figure out how to fix it. I would be very grateful to all the helpers.
int villageAmount;
cout << "Defining villages:\n\nEnter how many villages do you want: ";
cin >> villageAmount;
Village **villages;
for (int i = 0; i < villageAmount; i++)
{
cout << "\nDefining village's details #" << i + 1 << ":\n";
villages[i] = new (nothrow) Village;
}
Village **villages;
you declared villages without initializing, so it contains garabage valaue.
for (int i = 0; i < villageAmount; i++)
{
cout << "\nDefining village's details #" << i + 1 << ":\n";
villages[i] = new (nothrow) Village;
}
right below, you access into it and writes. That's no no.
If you want to store pointer of village (i.e. type of Village*) dynamically, you also need to allocate them.
Village **villages = new Village*[villageAmount];
Or if amount is fixed,
Village *villages[10] = { nullptr, }; // 10, 12344, or whatever
villages memory is never allocated, you should use a vector or any sort of container (an array look fine here).
If you really want to allow the memory manually you could do something like :
Village **villages = new Village*[villageAmount];
Don't forget to free EVERY new, (If you only free villages, all the Village* that he contain will remain allocated.
Edit : someone else answered so I guess my answere is usless
I'm trying to refactor some code to use pointers and am running into a write access violation on my function calls.
I'm making these edits because my homework project requires the usage of the -> member operator as well as constructors and destructors.
One more edit: the input file worked just fine when I was formerly working without pointers, but the moment I added pointers everything broke.
Here's my code:
In main.cpp:
#include "student.h"
int main()
{
/*
TODO:
2. Implement the class such that member pointers can be used to access the members.
3. Implement pointers that point to each of the students' test scores as well as the average test score.
*/
const int numStudents = 15; // Number of students
Student * students = new Student[numStudents]; // Dynamically allocated array of Student objects
Student ** studentsPtr = &students;
// Starting file stream for studentRecords.dat
ifstream student_records("student_records.dat");
// Error checking for file loading
if (!student_records)
{
cerr << "ERROR: The record file could not be opened for reading." << endl;
exit(1);
}
// Load data from file
string current_value;
stringstream newval;
int tempID;
string tempName;
double tempTestScore;
for (int index = 0; index < numStudents; index++)
{
// Store the student ID
getline(student_records, current_value);
newval << current_value;
newval >> tempID;
studentsPtr[index]->setID(tempID);
newval.clear();
// Store the student first name
getline(student_records, current_value);
newval << current_value;
newval >> tempName;
studentsPtr[index]->setFirstName(tempName);
newval.clear();
// Store the student last name
getline(student_records, current_value);
newval << current_value;
newval >> tempName;
studentsPtr[index]->setLastName(tempName);
newval.clear();
// Add each test score.
for (int testScoreIndex = 0; testScoreIndex < numTests; testScoreIndex++)
{
getline(student_records, current_value);
newval << current_value;
newval >> tempTestScore;
studentsPtr[index]->addTestScore(tempTestScore, testScoreIndex);
newval.clear();
}
// Calculate the student's average
students[index].calculateAverage();
}
// Print all data
for (int index = 0; index < numStudents; index++)
{
studentsPtr[index]->printAll();
}
delete[] students; // Free memory pointed to by students array
students = NULL; // Clear the memory.
system("pause");
return 0;
}
In student.h:
#pragma once
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;
const int numTests = 10;
// Student class declaration
class Student
{
private:
// Student ID and name
int id;
string firstName;
string lastName;
// List of student test scores
// vector<double> testScores;
double * testScores = new double[numTests];
// Student average test score
double average;
public:
Student() // Default constructor
{
const int numTests = 10;
id = 0;
firstName = " ";
lastName = " ";
average = 0.0;
}
~Student() // Destructor
{
delete[] testScores;
}
void setID(int); // Set the student ID
void setFirstName(string); // Set the student name
void setLastName(string);
void addTestScore(double, int); // Add a test score to the vector
void calculateAverage(); // Calculate the average of all test scores
void printAll(); // Output all data to the screen for a given student
};
In student.cpp:
#include "student.h"
// setID sets the id value.
void Student::setID(int studentID)
{
id = studentID;
}
// setName sets the name value.
void Student::setFirstName(string studentFirstName)
{
firstName = studentFirstName;
}
void Student::setLastName(string studentLastName)
{
lastName = studentLastName;
}
// addTestScore adds a test score to the vector
void Student::addTestScore(double testScore, int index)
{
testScores[index] = testScore;
// testScores.push_back(testScore);
}
// calculateAverage adds every test score from the vector and divides them by the number of test scores in the list.
void Student::calculateAverage()
{
double totalScores = 0.0;
// for (double index : testScores)
for (int index = 0; index < numTests; index++)
{
totalScores += testScores[index];
}
average = totalScores / numTests;
}
// printAll prints all the data to the screen.
void Student::printAll()
{
cout << "=========================================================\n";
cout << "Student ID:\t" << id << endl;
cout << "Student Name:\t" << firstName << " " << lastName << endl;
cout << setprecision(4) << "Average:\t" << average << endl;
cout << "Test Scores: " << endl;
// Printing the test scores nicely
int scoresPerLine = 5;
for (int i = 0; i < numTests; i++)
{
cout << setprecision(4) << testScores[i] << "\t";
if ((i + 1) % scoresPerLine == 0)
{
cout << endl;
}
}
cout << endl;
cout << "=========================================================\n\n";
}
The error I'm getting is Exception thrown: write access violation. this was 0xCCCCCCCC and it throws the exception at a break point created at
void Student::setFirstName(string studentFirstName) at the line firstName = studentFirstName.
My question is: what exactly is preventing this from working? Am I doing something wrong? I dont' get any errors before I compile everything, so it looks like everything is built ok. I've also tried using a pass-by-reference on that member function, but that's also failing with the same response.
Am I doing something wrong?
Yes, definitely :)
Let's go through it:
Student * students = new Student[numStudents];
... the above allocates a dynamic array of 15 Student objects; so far, so good.
Student ** studentsPtr = &students;
This line is the source of the trouble. You've declared a double-pointer Student ** and initialized it to point to the address of the students pointer. This is legal C++, but note that there is only the standalone students pointer -- in particular, there is not an array of pointers-to-Student in your program anywhere. (There is an array of Student objects but that is not the same thing as an array of pointers-to-Student)
... then a bit later on, comes the actual trouble:
for (int index = 0; index < numStudents; index++)
{
[...]
studentsPtr[index]->setID(tempID); // BOOM!
Here you are trying to use studentsPtr as if it was if it was the base-address for an array of pointers-to-Student, i.e. by offsetting its location by index pointers and dereferencing that location. But it's not really pointing to an array-of-pointers, it is pointing to a single pointer (i.e. it is pointing to the variable students), so whenever index is non-zero, you are invoking undefined behavior and therefore (in your case) you get a crash.
Let's debug it:
Since you didn't provide a complete test case, I changed the number of students to 3 and the number of tests to 0:
student_records.dat
1
Foo
Bar
2
Foo2
Bar2
3
Foo3
Bar3
My crash occurs in setID, but that's okay. this is 0xCCCCCCCC, which is the value given to uninitialized data by MSVC in debug mode. Great, the object pointer is garbage. Where does it come from? We'll go up the call stack to see:
This brings us to the following line in the loop of main that reads the input:
studentsPtr[index]->setID(tempID);
First, let's look at the variables:
We know the object is garbage. We can verify that here. Our object is studentsPtr[index], which is shown with the same uninitialized value. We also see that studentsPtr itself points to the proper first student. Finally, the index variable has the value 1, so we're on the second student.
studentsPtr[1] has a value that MSVC provides for uninitialized memory. Why is it uninitialized? Let's go back to the declaration:
Student *students = new Student[numStudents];
Student **studentsPtr = &students;
studentsPtr is set to be a pointer to a pointer to students. The inner pointer is actually an array of students. The outer pointer, however, is one solitary Student*. When indexing it like studentsPtr[1], we go beyond the single pointer within and trample onward to a nonexistent Student*. Then we try to write to that and the program thankfully blows up early.
The solution is to get rid of the double pointer. All that's needed is a bunch of Students. One pointer is one (unrecommended way to represent an) array:
Student *students = new Student[numStudents];
...
students[index].setID(tempID);
Now since the number of elements is known at compile-time, the recommended type would be std::array (std::array<Student, numStudents> students;), which can be used with the same syntax as the above after its declaration. If the size were not known at compile-time, the recommended type would be std::vector, which also shares the same syntax to access elements.
Technically, you can fulfill the -> requirement using a std::array as well. Simply obtain a pointer to the element and then use the arrow:
(&students[index])->setID(tempID);
More likely, the requirement is still looking for the manual free store memory management that you're doing. It's also easy to fit the arrow into that:
(students + index)->setID(tempID);
If you really, really need the double pointer even though it serves no purpose, remember that your array is the inner pointer, not the outer one:
((*students) + index)->setID(tempID);
If you're thinking the arrow hinders readability in all of these scenarios, you're correct. Perhaps the instructor has something specific in mind where it does not.
What happens when the double pointer is removed?
=========================================================
Student ID: 1
Student Name: Foo Bar
Average: -nan(ind)
Test Scores:
=========================================================
=========================================================
Student ID: 2
Student Name: Foo2 Bar2
Average: -nan(ind)
Test Scores:
=========================================================
=========================================================
Student ID: 3
Student Name: Foo3 Bar3
Average: -nan(ind)
Test Scores:
=========================================================
Success. The average is meaningless because I simplified the input file by changing the number of tests to 0. Long story short, the debugger provides the tools that can get debugging jobs done. Just from the debugger, we reduced the problem to the double pointer pointing to only one thing instead of multiple things. That's a much smaller scope for a problem than the original one.
This is a simple program I wrote:
using namespace std;
int main() {
string *word = new string[1]; //create a string object
*word = "blablabla"; //assign a string to that object
cout << "String: " << *word << endl;
delete word; //delete object? Causes unexected crash
int *ar = new int [10]; //create array of 10 blocks
ar[3] = 4; //assign a value to bl-3
cout << ar[3] << endl;
delete ar; //delete object, works
return 0;
}
Now from what I understand so far, one uses delete with new (as in delete one object that I created) and delete[] with new[] (delete and create an array of objects). The problem is that the former delete causes my program to crash while the latter works fine. Doing delete[] word works, however.
So how am I creating an array of objects? Am I mistaken in thinking that string *word = new string[1] creates just one object?
So how am I creating an array of objects? Am I mistaken in thinking that string *word = new string[1] creates just one object?
Sort of.
You are creating an array of 1 object.
You are creating one object. That's true. You are still creating an array.
Hence, you need to use the delete [] form.
delete [] word;
and
delete [] ar;
I have an array of Student objects. I set the array length to 100, but it doesn't have 100 valid Student objects in it. I want to be able to iterate through the array and grab all the valid Student objects, then stop when I get to an array cell that doesn't have a Student object.
I have tried putting NULL into the array cell after the last Student, and then checking if (queriedStudents[i]) as well as if(queriedStudents[i] != NULL), but neither has worked for me.
What is the best way to find the end of the used part of my array?
Student *Welcome::queryStudents(int *queries) {
int query = 0;
Student *matchedStudents[100];
int matchedPos = 0;
while (queries[query] > 0) {
for (int i = 0; i < numStudents; i++) {
if (allStudents[i]->id == queries[query]) {
matchedStudents[matchedPos] = allStudents[i];
matchedPos++;
}
}
query++;
}
matchedStudents[matchedPos] = NULL;
return *matchedStudents;
}
And my code chunk trying to print out each Student's values:
int i = 0;
while (i < 100) {
if (queriedStudents[i]) {
cout << "ID:\t" << queriedStudents[i]->id << endl;
cout << "Name:\t" << queriedStudents[i]->name << endl;
cout << "Addr.:\t" << queriedStudents[i]->address << endl;
cout << "Phone:\t" << queriedStudents[i]->phone << endl;
} else {
i = 100;
}
i++;
}
You've got a bigger problem. You declare the array matchedStudents on the stack in the function queryStudents. When control passes out of that function, the array passes out of scope. If you're trying to use it later (by means of the pointer it returns, which was the first element of the array) then you're messing with deallocated memory, which will almost certainly lead to undefined behavior. It's as if you're visiting a house that has changed owners since you were last there; there's no telling what's changed, and if you wander around with your eyes closed you might get into trouble.
You can declare the array on the heap:
Student **Welcome::queryStudents(int *queries) {
Student **matchedStudents = new *Student[100];
...
return matchedStudents;
}
Or pass it in by reference:
void Welcome::queryStudents(int *queries, Student **&matchedStudents) {
...
}
Either way, you can then tackle the problem of how to indicate the end of valid pointers. Your method looks feasible, but bear in mind that as #JerryCoffin has pointed out, std::vector is available. Arrays are a pain, and the STL containers (such as vector) were made to take care of these grubby details for you. These days working with arrays serves almost no purpose except pedagogy; play with them until you understand the concepts, then use more advanced containers which are base on them.
I have a question. I have the following struct:
typedef struct{
int vin;
char* make;
char* model;
int year;
double fee;
}car;
Then I have the following method that asks the user for the make of a car and returns it as a char pointer:
char* askMake(){
char* tempMake = NULL;
cout << "Enter Make:" << endl;
cin >> tempMake;
return tempMake;
}
Then I have a temp car struct:
car tempCar;
And I am trying to assign a value to it this way:
tempCar.make = askMake();
It compiles fine, but I get a segmentation fault at runtime.
You haven't allocated any memory for tempMake to point at. When you read in the data, it's reading it into whatever random location tempMake happens to point at.
Get rid of the pointers and use std::string instead to make life a lot simpler.
You have to allocate memory for tempMake.
Try this:
char* askMake(){
char* tempMake = new char[1024]; //Arbitrary size
cout << "Enter Make:" << endl;
cin >> tempMake;
return tempMake;
}
Don't forget to free with delete[] the memory that you allocated.
If you don't want memory leaks, you can avoid this using smart pointers like boost::shared_ptr or boost::scoped_ptr or similar. You can see more about this here.
You really want to use std::string here instead of char*. The problem is that you are trying to read user input into memory (tempMake) that has not yet been allocated.
std::string askMake(){
std::string tempMake;
cout << "Enter Make:" << endl;
cin >> tempMake;
return tempMake;
}
You will also probably want to use std::string instead of char* in your 'car' struct as well.
You're getting a segfault because you're writing to a null pointer. You should create a new memory space for cin to write to, then copy it when it returns. std::string can do this for you:
std::string askMake() {
std::string temp;
cout << "Enter Make:" << endl;
cin >> temp;
return temp;
}
Others have told you what needs to be done to fix the immediate problem: either allocate space for tempMake using new or malloc, or else use a std:string.
You probably don't want to return a pointer to a struct's member from a function. While you can make correct code while doing so, and there are also very good reasons to do so, this might not be one of those instances. The problem has to do with ownership. If you expose the variable by pointer, then the end user is free to pass that guy around into other functions that may eventually free it before you want them to, or change it in some other way. Additionally, what happens when you decide to free that memory yourself? What if the guy on your team who doesn't know your code was using that pointer value after you deleted it? What if nobody frees it and you use this struct over and over? This is a memory leak.
The best model is to hide this functionality is to no allow direct access to your class members, and don't return a pointer from a function unless absolutely necessary. In C++, I think the most elegant solution would be to return a std::string. In straight C, instead pass a char** (let's call it x) into the function, and do this:
int askMake(char** x)
{
char tempMake[100];//or some value you know to be large enough
cout << "Enter Make:" << endl;
cin >> tempMake;//i would use cin.get() so you know the length of the string.
//so let's pretend we have that length in a variable called stringLen.
*x = new char[stringLen];
for(int i = 0; x && i < stringLen; i++)
{
(*x)[i] = tempMake[i];
}
if(x)
return 0;
else
return 1;
}
As others have said, you're giving yourself extra work by using char* instead of std::string. If you switch over to std::string it would look like this:
#include <string>
struct car
{
int vin;
std::string make;
std::string model;
int year;
double fee;
};
std::string askMake()
{
std::string make;
cout << "Enter Make:" << endl;
cin >> make;
return make;
}
int main()
{
car tempCar;
tempCar.make = askMake();
}