vector with characters displaying - c++

I have a problem with displaying the data entered in register. The folowing program that I wrote displays just the last register.( ziua=day , inregistrari=registers, data=date (ex. 03.02.2013))
#include <iostream>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
int main()
{
char ziua[30],data[30],inregistrari[90];
int n,i;
cout<<"INPUT DATA"<<endl;
system("Pause");
cout<<"\nEnter the day in which you want to perform the register: ";
cin>>ziua;
cout<<"\nDATE:";
cin>>data;
cout<<"\nEnter the number of registers you wanna perfom for the day "<<ziua<<":";
cin>>n;
for(i=1;i<=n;i++)
{
cout<<"\nRegister "<<i<<":";
gets(inregistrari);
}
cout<<"The data for the day of "<<ziua<<" are the following: ";
cout<<"\nDATE: "<<data;
for(i=1;i<=n;i++)
cout<<"\n"<<inregistrari;
getch();
}

you are programming in C++, you should use std::string instead of C-style strings.
inregistrari[90] is an array of characters big enough to hold 1 string of max length of 89 chars (+ terminating character), but your loop seems to be treating it as an array or strings (although in this case gets(inregistrari); keeps on rewriting the same string)
function gets is generally deprecated, in C you should use fgets instead (yet this is C++, thus the real solution here should be using std::getline)
instead of C-style arrays, use std::vector<std::string> here.
printing the inregistrari is in the body of for loop, but each iteration of this loop does exactly the same thing (the printing does not depend on i in any way)
using namespace std; within the global space is a bad practice
you don't have to declare all variables at the beginning of the function, this was necessary in old ANSI C (about 20 years ago)
Here's an example how it could look like instead:
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::string day, date;
int registerCount;
std::cout << "INPUT DATA"
<< std::endl << std::endl
<< "Enter the day in which you want to perform the register: "
<< std::endl;
std::cin >> day;
std::cout << "DATE:" << std::endl;
std::cin >> date;
std::cout << "Enter the number of registers you wanna perfom for the day "
<< day << ":" << std::endl;
std::cin >> registerCount;
std::vector<std::string> registers(registerCount);
for (int i = 0; i < registerCount; ++i)
{
std::cout << "Register " << i << ":" << std::endl;
std::getline(std::cin, registers[i]);
}
std::cout << "The data for the day of " << day << " are the following: "
<< std::endl;
std::cout << "DATE: " << date << std::endl;
for (int i = 0; i < registerCount; ++i)
std::cout << registers[i] << std::endl;
}
Note that you might wrap std::getline(std::cin, registers[i]) with if statement and check whether a valid stream object has been return and in case of empty lines, it will read the empty string thus you might also make sure that !registers[i].empty().

Related

String doesn't want to store a 2700 character word

I'm trying to make a program that prints all the numbers from 100-999. After that you get to choose how many numbers you want to find. Then you type the number's position and it will be outputed.
There is one problem. The string, named str, stops storing at the number 954.
Here's the code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
//Prints to myFile the numbers from 100 to 999 without a space in between. Like this: 100101102...999
ofstream myFile("numere.txt");
for(int i = 100; i <= 999; i++)
myFile << i;
//Makes the string str to store the line: 100101102103...999. But only stores until 954 (100101102..954)
ifstream myFileRead("numere.txt");
string str;
while(getline(myFileRead, str))
cout << str << endl;
//Ouputs the lenght that should be 2700 but is instead 2565
cout << endl;
cout << "String legth: " << str.size() << endl;
cout << endl;
int n, k;
cout << "Enter how many numbers do you want to find: ";
cin >> n;
for(int i = 1; i <= n; i++){
cout << "Enter number position(it starts from 0) : ";
cin >> k;
cout << "Here's the number on position " << k << ": " << str.at(k);
cout << endl;
}
system("pause>0");
}
Thanks for your attention. I’m looking forward to your reply.
C++ streams are buffered. When you use << to write to a file it is not immediately written to the file.
Try to close or flush the ofstream before you read from it:
myFile.close(); // or...
myFile.flush();
For more details I refer you to flush() and close().
PS: Actually it is rather rare that you need to close a fstream explicitly. You wouldn't need to do it when you used seperate functions for writing and reading:
void write_to_file() {
std::ofstream myFile("numere.txt");
//...
}
void read_from_file() {
std::istream myFile("numere.txt");
//...
}
Because the destructor of ofstream already closes the file.

So I'm having trouble understanding files in C++

I just started learning files and I understand how to set it up and get it to work. I have to write this program where I have to allow the user to enter some information and have the user also update and adjust any data, using binary.
So I can write up until the point where the user can write to and read from the file. But I don't know how to let the user adjust data or add data.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class client {
public:
string name;
int balance;
string id;
};
int main()
{
int ans;
int x;
string nameIn;
string adjName;
client client1;
ofstream out("client1.dat", ios::binary);
cout << "\nDo you want to add information or update info" << endl;
cin >> ans;
if (ans == 1)
{
cout << "\nPlease enter the name of your client" << endl;
cin >> nameIn;
x = nameIn.length();
if (x <= 10)
{
for (int i; i < 10; i++)
{
adjName[i] = nameIn[i];
}
}
else
{
for (int i = x; i < 10; i++)
{
adjName[i] = ' ';
}
}
client1.name = adjName;
cout << "\nPlease enter the balance of your client" << endl;
cin >> client1.balance;
cout << "\nPlease enter the id of your client" << endl;
cin >> client1.id;
cout << "\nThe name of your client is " << endl << client1.name
<< endl << "\nThe balance of your client is " << endl
<< client1.balance << endl << "\nThe id of your client is "
<< endl << client1.id;
out.write(reinterpret_cast<const char*> (&client1), sizeof(client));
}
/*
else if (ans == 2)
{
string answer, newName,line;
cout << "\nWhat name do you want to update? " << endl;
cin >> answer;
cout << "\nWhat is the new name?" << endl;
cin >> newName;
if (out)
}
*/
system("pause");
return 0;
}
so the name needs to be only 10 characters long, so that we can adjust/update it. It compiles and runs, but every time the compiler gets to the part where it checks the name length, it freaks out and says "debug assertion failed"
string subscript out of range.
Also a thing about this code-- if i run it without the bits where you adjust the name to a certain array length, the program runs, and stores everything nicely. But when I try to read back the .dat, it reads it back but exits with an access violation, forcing me to manually stop the debugging. What am I doing wrong?
this is the code for reading the file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class client {
public:
string name;
int balance;
string id;
};
int main()
{
client client1;
char ans;
cout << "\nDo you want to view the information about your client?"
<< endl;
cin >> ans;
ifstream in("client1.dat", ios::binary);
if (ans == 'y' || ans == 'Y')
{
in.read(reinterpret_cast<char*> (&client1), sizeof(client));
cout << "The name is " << endl << client1.name << endl
<< "The balance is " << endl << client1.balance << endl
<< "The id is " << endl << client1.id << endl;
}
system("pause");
return 0;
}
As for the 1st part:
for (int i; i < 10; i++)
// ^
misses to initialize i to zero. Also what if the input was smaller than 10 characters? You're going to access the std::string out of bounds. You should replace the if/else and loops with simply
adjName = nameIn;
while(adjName.length() <= 10) {
adjName += ' ';
}
to get rid of the debug assertion.
For the 2nd part of the question, as already mentioned in the comments you cannot do this with a structure containing classes like std::string.
The reinterpret_cast<char*> (&client1) just obfuscates that std::string uses a pointer to the dynamically allocated character data internally, and that cannot be restored meaningfully when reading the stored data back later (hence the access violation you get).
A viable way might be to use something like
struct client {
char name[11];
int balance;
char id[5];
};
As I guess you need to do this for a homework exercise, and for this purpose that would probably be sufficient.
But you quickly can see the drawbacks, that the character data needs to be fixed in size and you cannot have arbitrary length strings. I never would use such for production ready code.
Another pitfall (as also mentioned) is, that int isn't represented in the same way (order of bytes used, i.e. endianess) in the same way for different CPU architectures. So the binary file can't be used portably with different computers.
The simplest solution is not to use a binary file, but a text formatted file and overload the std::ostream& operator<<(std::ostream&, const client&) and std::istream& operator>>(std::istream&, client&) output/input operators.
Or use some 3rd party library like boost::serialization or google protocol buffers, that supports de-/serialization to binary files.

std::_throw_out_of_range occurs from nowhere

I'm an absolute beginner in c++. Literally. It's just been a week.
Today I was writing a program to test how many iterations are needed to make a certain number palindromic.
Here is the code:
#include <iostream>
#include <string>
#include <algorithm>
/* This program calculates the steps needed
to make a certain number palindromic.
It is designed to output the values for
numbers 1 to 1000
*/
using namespace std;
class number
{
public:
string value;
void reverse();
};
void number::reverse()
{
std::reverse(value.begin(),value.end());
}
void palindrome(number num)
{
string n=num.value;
number reversenum, numsum, numsumreverse;
reversenum=num;
reversenum.reverse();
numsum.value=num.value;
numsumreverse.value=numsum.value;
numsumreverse.reverse();
int i=0;
while (numsum.value.compare(numsumreverse.value) !=0)
{
reversenum=num;
reversenum.reverse();
numsum.value=to_string(stoll(num.value,0,10)+stoll(reversenum.value,0,10));
numsumreverse.value=numsum.value;
numsumreverse.reverse();
num.value=numsum.value;
i++;
}
cout << "The number " << n << " becomes palindromic after " << i << " steps : " << num.value << endl;
}
int main()
{
number temp;
int i;
for (i=1; i<1001; i++)
{
temp.value=to_string(i);
palindrome(temp);
}
return 0;
}
It goes on smooth for numbers upto 195. But, in case of 196 I get an error.
It says:
terminate called after throwing an instance of 'std::out_of_range'
what(): stoll
I cannot make out what to do. I tried starting from 196 but the error persisted. Any help will be greatly appreciated. :)
UPDATE: This time I tried to do it using ttmath library. But arghs! It again stops at 195 and doesn't even report an error! I might be doing something foolish. Any comments would be appreciated. Here's the updated code:
#include <iostream>
#include <string>
#include <algorithm>
#include <ttmath/ttmath.h>
/* This program calculates the steps needed
to make a certain number palindromic.
It is designed to output the values for
numbers 1 to 1000
*/
using namespace std;
class number
{
public:
string value;
void reverse();
};
void number::reverse()
{
std::reverse(value.begin(),value.end());
}
template <typename NumTy>
string String(const NumTy& Num)
{
stringstream StrStream;
StrStream << Num;
return (StrStream.str());
}
void palindrome(number num)
{
string n=num.value;
number reversenum, numsum, numsumreverse;
reversenum=num;
reversenum.reverse();
numsum.value=num.value;
numsumreverse.value=numsum.value;
numsumreverse.reverse();
ttmath::UInt<100> tempsum, numint, reversenumint;
int i=0;
while (numsum.value.compare(numsumreverse.value) !=0)
{
reversenum=num;
reversenum.reverse();
numint=num.value;
reversenumint=reversenum.value;
tempsum=numint+reversenumint;
numsum.value=String<ttmath::UInt<100> >(tempsum);
numsumreverse.value=numsum.value;
numsumreverse.reverse();
num.value=numsum.value;
i++;
}
cout << "The number " << n << " becomes palindromic after " << i << " steps : " << num.value << endl;
}
int main()
{
number temp;
int i;
for (i=196; i<1001; i++)
{
temp.value=to_string(i);
palindrome(temp);
}
return 0;
}
UPDATE: It's solved. Some research suggested that 196 might be a Lychrel Number. And the result I was getting after implying the ttmath library is just reassuring that my algorithm works. I have tried it out for all the numbers upto 10000 and it gave out the perfect results. Here is the final code:
#include <iostream>
#include <string>
#include <algorithm>
#include <ttmath/ttmath.h>
#include <limits>
/* This program calculates the steps needed
to make a certain number palindromic.
It is designed to output the values for
numbers inside a desired range
*/
using namespace std;
string LychrelList;
int LychrelCount=0;
class number
{
public:
string value;
void reverse();
};
void number::reverse()
{
std::reverse(value.begin(),value.end());
}
template <typename NumTy>
string String(const NumTy& Num)
{
stringstream StrStream;
StrStream << Num;
return (StrStream.str());
}
void palindrome(number num)
{
string n=num.value;
number reversenum, numsum, numsumreverse;
reversenum=num;
reversenum.reverse();
numsum.value=num.value;
numsumreverse.value=numsum.value;
numsumreverse.reverse();
ttmath::UInt<100> tempsum, numint, reversenumint;
int i=0;
while ((numsum.value.compare(numsumreverse.value) !=0) && i<200)
{
reversenum=num;
reversenum.reverse();
numint=num.value;
reversenumint=reversenum.value;
tempsum=numint+reversenumint;
numsum.value=String<ttmath::UInt<100> >(tempsum);
numsumreverse.value=numsum.value;
numsumreverse.reverse();
num.value=numsum.value;
i++;
}
if (i<200) cout << "The number " << n << " becomes palindromic after " << i << " steps : " << num.value << endl;
else
{
cout << "A solution for " << n << " could not be found!!!" << endl;
LychrelList=LychrelList+n+" ";
LychrelCount++;
}
}
int main()
{
cout << "From where to start?" << endl << ">";
int lbd,ubd;
cin >> lbd;
cout << endl << "And where to stop?" << endl <<">";
cin >> ubd;
cout << endl;
number temp;
int i;
for (i=lbd; i<=ubd; i++)
{
temp.value=to_string(i);
palindrome(temp);
}
if (LychrelList.compare("") !=0) cout << "The possible Lychrel numbers found in the range are:" << endl << LychrelList << endl << "Total - " << LychrelCount;
cout << endl << endl << "Press ENTER to end the program...";
cin.ignore(numeric_limits<streamsize>::max(), '\n');
string s;
getline(cin,s);
cout << "Thanks for using!";
return 0;
}
It's a really awesome community. Special thanks to Marco A. :)
UPDATE AGAIN: I've devised my own add() function that cuts the program's dependency on external libraries. It resulted in a smaller executable and faster performance too. Here is the code:
#include <iostream>
#include <string>
#include <algorithm>
#include <limits>
/* This program calculates the steps needed
to make a certain number palindromic.
It is designed to output the values for
numbers inside a desired range
*/
using namespace std;
string LychrelList;
int LychrelCount=0;
string add(string sA, string sB)
{
int iTemp=0;
string sAns;
int k=sA.length()-sB.length();
int i;
if (k>0){for (i=0;i<k;i++) {sB="0"+sB;}}
if (k<0) {for (i=0;i<-k;i++) {sA="0"+sA;}}
for (i=sA.length()-1;i>=0;i--)
{
iTemp+=sA[i]+sB[i]-96;
if (iTemp>9)
{
sAns=to_string(iTemp%10)+sAns;
iTemp/=10;
}
else
{
sAns=to_string(iTemp)+sAns;
iTemp=0;
}
}
if (iTemp>0) {sAns=to_string(iTemp)+sAns;}
return sAns;
}
void palindrome(string num)
{
string n=num;
string reversenum, numsum, numsumreverse;
numsum=num;
numsumreverse=numsum;
reverse(numsumreverse.begin(),numsumreverse.end());
int i=0;
while ((numsum.compare(numsumreverse) !=0) && i<200)
{
reversenum=num;
reverse(reversenum.begin(),reversenum.end());
numsum=add(num,reversenum);
numsumreverse=numsum;
reverse(numsumreverse.begin(),numsumreverse.end());
num=numsum;
i++;
}
if (i<200) cout << "The number " << n << " becomes palindromic after " << i << " steps : " << num << endl;
else
{
cout << "A solution for " << n << " could not be found!!!" << endl;
LychrelList=LychrelList+n+" ";
LychrelCount++;
}
}
int main()
{
cout << "From where to start?" << endl << ">";
int lbd,ubd;
cin >> lbd;
cout << endl << "And where to stop?" << endl <<">";
cin >> ubd;
cout << endl;
string temp;
int i;
for (i=lbd; i<=ubd; i++)
{
temp=to_string(i);
palindrome(temp);
}
if (LychrelList.compare("") !=0) cout << "The possible Lychrel numbers found in the range are:" << endl << LychrelList << endl << "Total - " << LychrelCount;
cout << endl << endl << "Press ENTER to end the program...";
cin.ignore(numeric_limits<streamsize>::max(), '\n');
string s;
getline(cin,s);
cout <<endl << "Thanks for using!";
return 0;
}
You guys here have helped me a lot to find my own way. Thanks everyone. :)
You're overflowing long long since the last two valid values of num.value and reversenum.value are 7197630720180367016 and 6107630810270367917 which, added together, are way above the maximum size of a long long (9223372036854775807 on my machine). That will yield a negative value and spoil your next call to stoll
std::out_of_range is thrown if the converted value would fall out of the range of the result type or if the underlying function (std::strtol or std::strtoll) sets errno to ERANGE.
(reference)
If you're trying to get the next smallest palindrome, you should use another approach like the one I explained here.
You can find a Live Example here
If you prefer to/must continue with your approach you should either do the addition manually on the strings or use a bigint library (again take a look at here and modify the plusOne() function to your liking)
From http://www.cplusplus.com/reference/string/stoll/
If the value read is out of the range of representable values by a long long, an out_of_range exception is thrown.
The ll data type cant handle the string length. My debugger tells me 196 breaks on the value
std::stoll (__str=\"9605805010994805921-\", __idx=0x0, __base=10)
The long long is too small.
You might want to do the addition on the strings themselves, without resorting to a numeric type.

Better way to loop through user input

So I've been working on a little program that asks the user for the name/hours studying for a given topic, then does some calculations and displays the results back. Right now, to get the input for, say, 4 topics, I'm doing it this way:
#include <iostream>
using namespace std;
int main()
{
string topic1 = "";
string topic2 = "";
string topic3 = "";
string topic4 = "";
/* could have more topics here */
double hoursStudying1 = 0;
double hoursStudying2 = 0;
double hoursStudying3 = 0;
double hoursStudying4 = 0;
cout << "Topic name: ";
getline(cin, topic1);
cout << "Time studying for " << topic1 << ": ";
cin >> hoursStudying1;
cin.ignore();
cout << "Topic name: ";
getline(cin, topic2);
cout << "Time studying for " << topic2 << ": ";
cin >> hoursStudying2;
cin.ignore();
cout << "Topic name: ";
getline(cin, topic3);
cout << "Time studying for " << topic3 << ": ";
cin >> hoursStudying3;
cin.ignore();
/* calculate stuff here */
/* display the results */
cout << "For " << topic1 << " you spent " << hoursStudying1 << " hours studying" << endl;
/* etc */
return 0;
}
I'm a beginner, but I know there has to be a more efficient way to do this :(. How could I use, for example, a do...while loop or even a for loop to display a set number of prompts for each topic/hours studied (in order) as I'm doing now? I'm not looking for anyone to rewrite my program or anything like that, but rather give me some pointers with their code. Thanks a lot guys!
Edit: thank you so much for all the examples and pointers you've given me. I will play around with them and post some code of my own to let you know how it's working.
You will need to use an array or list, then loop through them. You can have two separate arrays, one to hold the topic names, and the other to hold the hours studies, or you can define a class that includes a topic name and hours studied and then have an array of elements of that class.
Bottom line, you need to learn how to use arrays or collections.
You can use a struct
typedef struct _element {
string topic;
double hoursStudying;
} element;
Then you can define an array of elements like this
element journal[ 4 ];
Now you can think of using loops to handle the array journal.
Try it yourself: write a complete program out of the above hints and let us know...
After doing that, try to transform the struct into a class figuring out what member functions are needed and then implementing them. Again, let us know...
I'd do something like:
cout << "Enter topic name or 'quit'";
while(true)
{
cin<< foo;
if(foo == 'quit')
break;
else
{
//Do stuff with foo
}
}
Obviously you'll want to handle weird input ("qUiT"), but that's the loop structure.
How could I use, for example, a do...while loop or even a for loop to display a set number of prompts for each topic/hours studied (in order) as I'm doing now?
Instead of creating a separate variable for, you should create an array to store your data. After that, use a loop to prompt and receive input. It can be a while-loop or a for-loop. (Generally, use a for-loop when you are certain how many times you want it to iterate. Use a while-loop when the number of times to iterate cannot be determined)
Example:
double hoursStudying[5] = {0};
for(int x=0; x<5; x++){ //5 can be replaced with a variable indicating array size
cout << "Time studying for " << topic << " " << x << ": ";
cin >> hoursStudying[x];
cin.ignore();
}
For displaying output, it will be the same. Just use a loop to iterate through the array values.
Example:
for(int x=0; x<5; x++){
cout << "For " << topic << " " << (x+1) << " you spent "
<< hoursStudying[x]<< " hours studying" << endl;
}
This is proper way to write programs in C++:
File Topic.h:
#pragma once
#include <sstream>
#include <string>
using namespace std;
class Topic {
string name;
double hours;
public:
Topic(string name, double hour);
~Topic();
void setName(const string & _name) {
name = _name;
}
void setHours(double & _hours) {
hours = _hours;
}
void addHours(double & _hours) {
hours += _hours;
}
string getName() const {
return name;
}
double getHours() const {
return hours;
}
string print() const;
};
File Topic.cpp
#include "Topic.h"
Topic::Topic(string name = "", double hour = 0.0) :
name(name), hours(hour) {
}
Topic::~Topic() {
}
string Topic::print() const {
ostringstream output;
output << "For " << name << " you spent " << hours << " hours studying.";
return output.str();
}
File Topics.h:
#pragma once
#include <vector>
#include <sstream>
#include <string>
#include "Topic.h"
using namespace std;
class Topics {
vector<Topic> topics;
public:
Topics();
~Topics();
void add(const Topic & topic) {
topics.push_back(topic);
}
Topic get(const unsigned int & index) const {
return topics[index];
}
string printAll() const;
};
File Topics.cpp:
#include "Topics.h"
Topics::Topics()
{
}
Topics::~Topics()
{
}
string Topics::printAll() const {
ostringstream output;
for (Topic topic : topics) {
output << topic.print() << endl;
}
return output.str();
}
File Source.cpp
#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include "Topic.h"
#include "Topics.h"
using namespace std;
Topic input() {
string name;
double hours;
cout << "Topic name: ";
cin >> name;
cout << "Time studying for " << name << ": ";
cin >> hours;
cout << endl;
return Topic(name, hours);
}
Topics inputCycle()
{
Topics topics;
int n;
cout << "How many topics you have ?" << endl;
cin >> n;
for (int i = 0; i < n; i++)
topics.add(input());
return topics;
}
void printAll(const Topics & topics) {
cout << topics.printAll() << endl;
}
int main()
{
printAll(inputCycle());
system("PAUSE");
return false;
}

Storing user input data in a dynamic array of structures/ displaying said data. C++

So for my project I have to...
Create a structure using the format: struct PERSON{string name; int age; float gpa;};
Ask the user to enter the number of records they would like to enter.
Create a dynamic array p of PERSON type to be able to hold all records in part 2
Read data into array p in part 2
Display array p
However, whenever I run my program and begin to enter my data it won't let me enter more than one set of data and the output seems to make little sense.
Here is my code:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
struct PERSON
{
string name;
int age;
float gpa;
};
PERSON *p;
int main()
{
int count;
cout << "Please enter the number of records you wish to enter: ";
cin >> count;
p = new (nothrow) PERSON[count];
if (p == nullptr)
cout << "Error: memory could not be allocated";
for (int i = 0; i < count; i++)
{
cout << "Enter name, age, and gpa: ";
getline(cin, p[i].name);
cin >> p[i].age;
cin >> p[i].gpa;
cout << endl;
}cout << endl;
cout << "This is the content of array p: " << endl;
cout << right << setw(8) << "NAME" << setw(7) << "AGE" << setw(7) << "GPA" << endl;
cout << "--------------------------" << endl;
for (int i = 0; i < count; i++)
{
cout << p[i].name << " " << p[i].age << " " << p[i].gpa << endl;
}
return 0;
}
Now, this format seemed to work just fine when I was copying data from a file into a dynamic array of structures, but I can't seem to get it to work now that I'm dealing with user input.
Here is what a trial run of the program produces:
Please enter the number of records you wish to enter: 3
Enter name, age, and gpa: Robert 21 2.1 // This is the info I tested
Enter name, age, and gpa:
Enter name, age, and gpa:
This is the content of array p:
NAME AGE GPA
--------------------------
-842150451 -4.31602e+008 //?
-842150451 -4.31602e+008 //?
-842150451 -4.31602e+008 //?
Press any key to continue . . .
I've probably gone over the code two dozen times now and have been searching for an answer for quite a while. Any advice/analysis/comments would be greatly appreciated.
So #AndyG suggested that I change the getline(cin, p[i].name) to cin >> p[i].name and that seemed to clear things up. Thank you!
Sorry to trouble everyone over such a silly muck-up.
The Problem
Your getline(cin, p[i].name) is actually grabbing all your input on the same line, and your later cin statements are going to be mismatched.
std::getline should be reserved for getting an entire line into a character buffer or std::string, i.e., it's not always the appropriate tool for reading in a string.
The Solution
Instead, you simply need to replace getline(cin, p[i].name) with cin >> p[i].name.
Live Demo
Other minor changes
Remove the nullptr check:
if (p == nullptr)
cout << "Error: memory could not be allocated";
Because in C++ you will get an exception thrown if the new operator fails to allocate. Like bku_drytt mentioned, you can wrap this in a try catch block instead, although I'd personally just remove it altogether.
#include <new>
// ...
Person* p = null;
try
{
p = new Person[count];
} catch(std::bad_alloc& ex)
{
cerr << "Unable to allocate memory for Person! Error msg: " << ex.what() << "\n";
return 1;
}
Finally, don't forget to delete[] the memory you allocated for p:
// ...
delete[] p;
return 0;
} //end of int main()
Even though your OS will most definitely clean that memory up for you, you should still ensure you clean it up manually. Good practice at least. Alternatively, you can use a std::array or a std::vector instead, which will still give you random access just like the c-style array of PERSON you already have.
The std::getline() function is "misbehaving" in relation to your intentions because there is (I think) a new line character left in the stream input queue.
You can discard such characters by using the following command cin.sync().
Why does this happen? Because operator>> does not read whitespaces, but std::getline() does, so when the cin >> count; statement is executed and you press enter, the new line character (from pressing enter) is left in the stream input queue, which is automatically read by std::getline() upon execution.
There are other functions that can accomplish this and perhaps someone more informed can tell us which is the best choice.
Here is a fixed version:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
struct Person
{
string name;
int age;
float gpa;
};
int main()
{
cout << "Please enter the number of records you wish to enter: ";
int count;
cin >> count;
Person* p = new Person[ count ];
for ( int i = 0; i < count; i++ )
{
cin.sync(); // added this to discard unused characters from input queue
cout << "Enter name: ";
getline( cin, p[ i ].name );
cout << "Enter age: ";
cin >> p[ i ].age;
cout << "Enter gpa: ";
cin >> p[ i ].gpa;
cout << endl;
}cout << endl;
cout << "This is the content of array p: " << endl;
cout << right << setw( 8 ) << "NAME" << setw( 7 ) << "AGE" << setw( 7 ) << "GPA" << endl;
cout << "--------------------------" << endl;
for ( int i = 0; i < count; i++ )
{
cout << p[ i ].name << " " << p[ i ].age << " " << p[ i ].gpa << endl;
}
return 0;
}
Notes:
You do not need to check if Person* p is nullptr because the new operator will throw a std::bad_alloc exception if it fails to allocate enough memory. Consider wrapping that memory allocation in a try catch block if you really want to.