I'm refreshing my knowledge of C++ dynamic memory allocation and structs, and suddenly ran into some trouble. Below is the part of code, which stops executing after 3 line and the program terminates.
int n;
std::cout << "How many hotels do you want : ";
std::cin >> n;
hotel* hotels= new (nothrow) hotel[n];
for (int i= 0; i< n; i++) {
std::cout << "Hotel " << i+1 << " name : ";
std::cin >> hotels[i].name;
std::cout << "Hotel " << i+1 << " rating : ";
std::cin >> hotels[i].rating;
std::cout << "Hotel " << i+1 << " stars : ";
std::cin >> hotels[i].stars;
}
Here is the "hotel" declaration:
struct hotel {
char* name;
short int rating, stars;
};
I'm guessing there is something wrong with the dynamic declaration of "hotels". Where have I gone wrong?
Here, you need to allocated your char*. If not you will have an undefined behaviour (generally a segFault)
There is two more things in your code :
You should use std::string instead of char*. It is a better practice in C++. (At least in this case) :
#include <string>
struct hotel {
std::string name;
//^^^^^^^^^^^
short int rating, stars;
};
You may also want to use std::vector.
The issue here is that you need to allocate memory for char* name in your struct in order to store characters.
You can also use string instead of char * if you are using C++ (preferred way):
struct hotel {
string name;
short int rating, stars;
};
You'll need to include new to be able to use nothrow
http://www.cplusplus.com/reference/new/nothrow/
#include <new> //std::nothrow
Related
I'm working on an assignment right now and when run my code returns this error:
main.cpp:60:20: error: ‘dataArr’ was not declared in this scope
if(tolower(dataArr[i].last) == tolower(lastName))
I'm not quite sure what I'm missing here. If I could at least get it to run I'd appreciate it. Thanks.
I thought arrays were declared globally so i thought it wouldn't be an issue in my functions
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Database
{
string first;
string last;
string ID;
string phoneNum;
};
void lastSearch(string);
void idSearch(string);
int main()
{
Database dataArr[100];
ifstream myFile("library_database.txt");
int count = 0;
while(!myFile.eof() && count < 100)
{
myFile >> dataArr[count].first >> dataArr[count].last >> dataArr[count].ID >> dataArr[count].phoneNum;
cout << dataArr[count].first << " " << dataArr[count].last << " " << dataArr[count].ID << " " << dataArr[count].phoneNum << endl;
count++;
}
int input;
string search;
cout << "Would you like to search by last name or member ID?\n1. Last Name\n2. ID\n> ";
cin >> input;
while(input != 1 || input != 2)
{
cout << "Enter a valid answer.\n> ";
cin >> input;
}
if(input == 1)
{
cout << "Enter last name: ";
cin >> search;
lastSearch(search);
}
if(input == 2)
{
cout << "Enter ID: ";
cin >> search;
idSearch(search);
}
return 0;
}
void lastSearch(string lastName)
{
int num = 0;
for(int i = 0; i < 100; i++)
{
if(tolower(dataArr[i].last) == tolower(lastName))
{
cout << dataArr[i].first << " " << dataArr[i].last << " " << dataArr[i].ID << " " << dataArr[i].phoneNum << endl
num++;
}
}
if(num == 0)
{
cout << "No match was found in the file.";
}
}
voidSearch was removed to allow this to be posted
To answer the title of your post: because it isn't.
You declare dataArr in main, but you are trying to use it in lastSearch, so lastSearch can't see it. But you can pass it in as a parameter, that's probably the easiest fix:
void lastSearch(const string lastName, const Database *dataArr) { ... }
and call it like this:
lastSearch (search, dataArr);
Note the use of const (get into the habit of doing that whenever you can) and that your array 'decays' to a pointer when you pass it as a parameter like this, so don't be tempted to use sizeof in lastSearch. If you need to know the number of elements in the array, pass that as a parameter too.
Or, better, use std::array instead of a C-style array and then the size of the array is available in lastSearch without the need to pass it in separately. If you do that, you probably want to pass it by const reference to avoid copying it every time you call the function.
Finally, it might be time to learn about std::vector. At the expense of a little more complexity (but not much), this would avoid the need to allocate a fixed size array. Again, for the same reason, pass it around by reference.
Some bedtime reading: The Definitive C++ Book Guide and List
Arrays are not declared globally, they are declared where you declare them :-)
In your case, you declare it at the top of main() so that is its scope, from point of declaration to end of main(). Trying to use it in lastSearch() is therefore invalid.
The easiest fix is probably just to move the declaration immediately before main() so that it is global. But the easiest things is often not the right thing.
You would be better off embracing C++ fully(1) and using something like std::vector, whose size isn't arbitrarily limited to 100 (for example) and which you could pass around quite easily, something like:
#include <iostream>
#include <vector>
void function(const std::vector<int> &vec) {
std::cout << vec.size() << ' ' << vec[0] << '\n'; // Output: 2 42
}
int main() {
std::vector<int> x;
x.push_back(42);
x.push_back(99);
function(x);
}
The main advantages with vectors are that:
you're not limited to a maximum of 100 items;
you don't have to pass around the actual count of items read separately as with a raw array or even a std::array (you don't do that in your code but I assure you, that's a problem).
the size of the vector is an integral property of the vector, available anywhere the vector is in scope.
(1) There's a variety of developers I like to call C+ developers. These are the people that, though they claim to be C++ developers, have never really embraced the C++ way of doing things, sticking to C style programming practices like non-smart pointers or normal arrays :-)
Some of those things may still have a place in modern C++ code but you should be circumspect in their use.
#include <iostream>
using namespace std;
class car{
string owner;
string car_num;
string issue_date;
car(string o, string cn, string id)
{
owner = o;
car_num = cn;
issue_date = id;
}
void getInfo()
{
cout << "Car's Owner's Name : " << owner << endl;
cout << "Cars' Number : " << car_num << endl;
cout << "Car's Issue Date : " << issue_date << endl;
}
};
int main()
{
int n;
cout << "Enter total number of cars stored in your garage : \n";
cin >> n;
car c1[n]; //incomplete code due to the issue
return 0;
}
Here I want to take the total car numbers from user. And also want to take the car properties from user by using a loop. But how Can I do that while using a constructor?
My advice is not to over use the constructor. It supposed to construct, and really should only construct. In your case, you don't even really need a constructor.
Instead add a new function to do initialization.
Traditional is to use an operator >> which is often an external function.
As for the loop...
car c1[n]; //incomplete code due to the issue
is not legal C++ (although it's allowed in C, and many compilers that are also C compilers)
It's better to use a vector. So...
vector<car> c1(n);
for (auto& c : c1)
cin >> c;
An advanced technique is to use a istream iterator, which will allow you to use algorithms like std::copy, calling the input operator for each member of the vector. However, it's really not required, just a "nicety"
car c1[n]; //incomplete code due to the issue
In fact, you have 2 issues here:
Variable-Length Arrays (VLA) are not allowed in standard C++. They
are optionally allowed in standard C, and are supported by some
C++ compilers as an extension.
You can't have an array of objects w/o default constructor (unless you fully initialize it).
Assuming you don't want to change the class (other than insert public: after the data members), the modern solution should use std::vector:
std::vector<car> c;
//incomplete part
for(int i = 0; i < n; i++){
std::string owner, car_num, issue_date;
//TODO: get the strings from the user here ...
c.emplace_back(owner, car_num, issue_date);
}
Use pointer array instead.e.g.
car* c1[n];
//incomplete part
for(int i = 0; i < n; i++){
//take input and process
c1[i] = new car(//put processed inputs here);
}
PS: I feel like I made a mistake somewhere but can't test it now. If it doesn't work, put a comment here.
You can use loop with std::cin, likefor(int i=0;i<n;++i){std::cin<<num;}. The 'n' I mentioned in the code can also be assigned by std::cin
int n;
std::cin>>n;
car* cars=new car[n];
for(int i=0;i<n;++i)
{
std::getline(cars[i].owner,std::cin);
// and something other you'd like to do, like to test its validity
}
#include <iostream>
using namespace std;
class car{
public:
string owner;
string car_num;
string issue_date;
void cars(string o, string cn, string id)
{
owner = o;
car_num = cn;
issue_date = id;
getInfo();
}
void getInfo()
{
cout << "Car's Owner's Name : " << owner << endl;
cout << "Cars' Number : " << car_num << endl;
cout << "Car's Issue Date : " << issue_date << endl;
}
};
int main()
{
int n;
string a,b,c;
cout << "Enter total number of cars stored in your garage : \n";
cin >> n;
car cas[n]; //incomplete code due to the issue
for(int i=0;i<n;++i)
{
cout<<"value1:";
cin>>a;
cout<<"value2:";
cin>>b;
cout<<"value3:";
cin>>c;
cas[i].cars(a,b,c);
}
return 0;
}
I'm collecting names and test scores to populate a vector. Both the function and main method can't recognize the struct's members. How can I get it to see the members? Or is there a better way to populate a vector of structs with user input using a function?
I've searched other similar posts, but it seems like it's just a simple code error I missed.
#include <iostream>
#include <vector>
using namespace std;
const int classSize = 1;
struct StudentType {
string studentFName;
string studentLName;
int testScore;
char grade;
};
vector<StudentType> collectStudentData(vector<StudentType> students[classSize]) {
for (int i = 0; i < classSize; i++) {
cout << "Student " << i << "'s name and test score" << endl;
cin >> students[i].studentFName >> students[i].studentLName >> students[i].testScore;
}
return students[classSize];
};
int main() {
vector<StudentType> students[classSize] = {};
students[classSize] = collectStudentData(students);
cout << students[1].studentFName << students[1].studentLName << students[1].studentFName;
};
'studentFName': is not a member of 'std::vector>'
This line creates an array of vectors:
vector<StudentType> students[classSize] = {};
What you want is this a single vector:
vector<StudentType> students;
Where that gets initialized to a zero-length array.
When it comes to adding data you don't need to return from the other method, you can pass in a reference and add to it:
void collectStudentData(vector<StudentType>& students) {
for (int i = 0; i < classSize; i++) {
// Read in one at a time
StudentType student;
cout << "Student " << i << "'s name and test score" << endl;
cin >> student.studentFName >> student.studentLName >> student.testScore;
// Add to the array
students.push_back(student);
}
}
Ideally classSize is either passed in as an argument, or you just type a blank line to end input. Using a global variable is really messy and should be strongly discouraged.
vector<StudentType> students[classSize]
Is one issue. You are not declaring a function that takes a vector, you are declaring a function that takes an array of vectors.
Secondly, if you only applied that change you would be passing an empty vector, you can initialize vector to be a particular size by passing in the size to the constructor.
Furthermore, it seems that you would benefit from passing the students vector by reference
vector<StudentType>& students
instead, the & creates a reference. Right now your code is copying the vector when it is passed into the function
#include <iostream>
#include <vector>
using namespace std;
const int classSize = 1;
struct StudentType {
string studentFName;
string studentLName;
int testScore;
char grade;
};
void collectStudentData(vector<StudentType>& students) {
for (int i = 0; i < classSize; i++) {
cout << "Student " << i << "'s name and test score" << endl;
cin >> students[i].studentFName >> students[i].studentLName >> students[i].testScore;
}
return students;
};
int main() {
vector<StudentType> students{classSize};
collectStudentData(students);
cout << students[0].studentFName << students[0].studentLName << students[0].studentFName;
};
If you wanted to improve the code further, you would use an iterator in the for loop instead, and preferably you wouldn't need to construct the vector in main, and pass it into a function to mutate it. You could just construct it and return it from the function.
I've seen a couple threads dealing with similar errors, but they were all dealing with different variations of the problem from the one I have encountered (code for: "I'm too green to make sense of them").
#include <iostream>
#include <array>
#include <sstream>
using namespace std;
struct StudentRecord
{
char name[20];
int id;
float gpa;
};
int main()
{
cout << "Enter your name:" << endl;
char nameInput[20];
cin >> nameInput;
cout << "Enter your student ID:" << endl;
int idInput;
cin >> idInput;
cout << "Enter your GPA:" << endl;
float gpaInput;
cin >> gpaInput;
StudentRecord TESCStudent =
{ { nameInput }, idInput, gpaInput }; // TROUBLE STARTS HERE
cout << "Name: " << TESCStudent.name << "\nID: " << TESCStudent.id << "\nGPA: "
<< TESCStudent.gpa << endl;
}
The StudentRecord TESCStudent = {...} initializer gives the following errors:
error C2440: 'initializing' : cannot convert from 'char [20]' to 'char'
IntelliSense: a value of type "char *" cannot be used to initialize an entity of type "char"
I realize that an string would solve a lot of problems, but the project asked that we get the name as a 20 character array.
This is my first time using a struct, so I may be doing something horrendous without meaning to, please go easy on me!
You wrote:
StudentRecord TESCStudent = { { nameInput }, idInput, gpaInput };
Basically: you can't do that. C-style arrays have unintuitive syntax and semantics; and there is no way to initialize the elements of a C-style array using another C-style array other than by listing each element individually.
My advice would be to not use them at all. If you used std::string instead of the name array then you could simply write StudentRecord TESCStudent = { nameInput , idInput, gpaInput };. This would also fix the problem that your code causes a buffer overflow if the person types in a name of length 20 characters or more.
If you are forced to stick with the char array due to course requirements then I'd suggest avoiding the issue by reading directly into the array, and adding overflow protection and error checking, for example:
StudentRecord TESCStudent = {};
cout << "Enter your name:" << endl;
cin >> setw(sizeof(TESCStudent.name)) >> TESCStudent.name;
cout << "Enter your student ID:" << endl;
cin >> TESCStudent.id;
cout << "Enter your GPA:" << endl;
cin >> TESCStudent.gpa;
if ( !cin )
{
cout << "Invalid input, sorry.\n";
return EXIT_FAILURE;
}
#yinch rightly pointed out that I was treating idInput as a value instead of an array. Writing it as:
StudentRecord TESCStudent = { { *nameInput }, idInput, gpaInput };
removes the error, but only adds the first value from nameInput[20] to TESCStudent.name[20]. I decided to add the values to TESCStudent like this:
StudentRecord TESCStudent; // create a variable from StudentRecord
cout << "Enter your name:" << endl; // prompt for name
char nameInput[20];
cin >> nameInput;
for( size_t i = 0; i < 20; i++ ) // assign each element of nameInput
// to TESCStudent.name explicitly
{
TESCStudent.name[ i ] = nameInput[ i ];
}
cout << "\nEnter your student ID:" << endl; // prompt for ID
int idInput;
cin >> idInput;
TESCStudent.id = idInput; // assign idInput to TESCStudent.id
cout << "\nEnter your GPA:" << endl; // prompt for GPA
float gpaInput;
cin >> gpaInput;
TESCStudent.gpa = gpaInput; // assign gpaInput to TESCStudent.gpa
The for loop solves the problem by assigning each array value to TESCStudent.name explicitly. Thanks for the help!
Changing the problem line to:
StudentRecord TESCStudent =
{ { *nameInput }, idInput, gpaInput };
fixes the problem.
The thing to remember about strings is that they are, themselves, arrays.
So In your example, "nameInput" by itself doesn't hold a char value, it actually holds a pointer to the first char value.
Distinguishing between variables (which hold value) and pointers (which are address pointers pointing to locations that hold the value) is a crucial part of the difference between strings and other literal values (like int and float)
By pre-fixing the nameInput (pointer) with an asterisk indicates that you want the value that the pointing at.
I have an exercise to write down data to a dynamic table of structures using a function. Here's my code:
#include <iostream>
#include <cstdlib>
using namespace std;
struct student{ char name[15], surname[20]; int age; };
student * createTab(int tsize)
{
student *t = new student[tsize];
return t;
}
void fill(student *t, int tsize)
{
for (int i = 0; i<2; i++)
{
cout << "Enter a name: "; cin >> t[i].name;
cout << "Enter a surname: "; cin >> t[i].surname;
cout << "Enter age: "; cin >> t[i].age;
}
}
int main()
{
student *t = createTab(10);
fill(t, 20);
cout << t[0].surname << endl;
cout << t[1].name << endl;
system("pause");
delete[]t;
return 0;
}
It works, okay. But here, in fill() function I use the index syntax with student[].name. I always worked on tables with pointers like that: *(table+i) in a for loop. *(t+i).name doesn't work. Can I iterate on structure fields using pointers?
P.S - Am I freeing the memory correctly?
And I guess P.S 2 - How is it possible, that when I insert a pointer to a first element of my table to a function, and then I can operate on whole table with indexes?
The standard defines the subscripting as follows:
5.2.1/1 (...) The expression E1[E2] is identical (by definition) to *((E1)+(E2))
This is why, using a pointer t and an index i, *(t+i) and t[i] is the same. The problem with your code in the context of struct fields, is a question of priority: you may write (*(t+i)).name or better (t+i)->name, or much clearer, as you did: t[i].name.
P.S.: If you allocate a table with new[...] you have to free it with delete[]. So yes: it's ok !