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();
}
Related
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.
void main()
{
char *str[3];
cin>>str[0];
cin>>str[1];
cout<<str[1];
}
Output:
Hi
Hello
After the output, it either hangs or gives me a null-pointer assignment error. Why is this?
You have allocated three pointers to char strings, but no memory for the actual strings. Until you understand pointers better, I suggest you just use something like this:
char one[10];
char two[10];
char three[10];
cin >> one;
cin >> two;
cout << two;
You are using str[0], for example. This is a char pointer, or a pointer to one or more characters.
However, you haven't pointed it to anything. And so it constitutes undefined behavior to access a NULL or random address from your code.
You need to better understand pointers, and make sure your pointers actually point to something.
You have not allocated any memory for your C-Style strings.
Switch to std::string if you want to use strings without the memory allocation and deallocation hassles (to name a few).
Also, the main function returns int. Always.
Try this:
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::string;
int main(void)
{
string str[3];
cin >> str[0];
cin >> str[1];
cin >> str[2];
cout << "[0]: " << str[0] << "\n";
cout << "[1]: " << str[1] << "\n";
cout << "[2]: " << str[2] << "\n";
return EXIT_SUCCESS;
}
Also, consider using std::vector instead of arrays.
I'm getting an error:
expression must be a modifiable lvalue at line, obj.name = ptr->name
I have tried to make obj an array type object like so
for(int j=0 ;j<1;j++)
{
obj[j].id = ptr->id;
obj[j].balance= ptr->balance;
obj[j].name = ptr->name; //still getting error here.
obj[j].nic = ptr->nic;
}
return(obj);
}
but that has not worked either.
if i comment the error out and pass just three remaining values it should work but i receive garbage values after the first out put.
here is the original code:
#include<iostream>
using namespace std;
struct bank
{
int id, nic;
float balance;
char name[20];
};
bank search(bank* );
void main()
{
bank data[2],mobj;
for(int i=0;i<2;i++)
{
cout<<"enter name: ";
cin>>data[i].name;
cout<<"enter id: ";
cin>>data[i].id;
cout<<"enter balance : ";
cin>>data[i].balance;
cout<<"enter nic : ";
cin>>data[i].nic;
}
mobj=search(data);
cout <<"balance of customer no. "<<mobj.balance<<endl;
cout<<"id is" <<mobj.id<<endl;
cout<< "nic is"<<mobj.nic<<endl;
system("pause");
}
bank search(bank *ptr)
{
int id;
cout<<"enter value you want to serch"<<endl;
cin>>id;
bank obj;
for(int i=0 ; i<2 ;i++)
{
if(ptr->id == id)
{
break;
}
ptr++;
}
obj.id = ptr->id;
obj.balance= ptr->balance;
obj.name = ptr->name; //error in this line(obj must be modifiable value)
obj.nic = ptr->nic;
return(obj);
}
please help as you see fit!
obj.name is an array of char. You cannot do assignments on arrays. So if you want to stick to arrays:
see c++ array assignment of multiple values
use strcpy(obj.name, ptr->name);
but I'd recommend to convert to std::string ... it's much easier to work with than arrays and it seems to me you plan to use obj.name as string. So got with the proper strings.
Your options are:
Have name as a std::string. Then it will work
use strcpy( obj.name, ptr->name );
If your code is C++ then the first has even more advantages that when you read it in with cin it will automatically ensure that the buffer is big enough for what the user enters.
Try entering a name of 20 or more characters now and see what happens. You will have undefined behaviour and your program may crash.
Those are the fixes anyway.
The reason your code does not work is that an array is not assignable. It is not an l-value. It is the start of a fixed array of bytes. I know it is confusing because you can use = to initialise it thus you can do:
char name[20] = "Frank Smith"; // legal. This is initialisation
but you can't do:
char name[20];
name = "Frank Smith"; // error. This is attempted assignment.
With std::string it works as string is a class in the standard library. (Actually the class is basic_string< char, char_traits<char>, allocator<char> > but assume it is a class for now). And it has methods that know how to assign to it, stream into it, etc. and manage the memory properly.
The only time the array in your struct is more useful is if you want to store it to disk or send it over a connection where you need "raw" data, or where you want to interface it with "C" or a scripting language that works with C structs.
obj[j].name is an array. You can't copy an array like that.
this is totally strange. I have some code in which I read some parameters from a file and I store them in two stl vectors. I have atoms and residues, and every atom keeps a pointer to his residue. Once finished reading, after declaring a variable, looks like the values in memory changed:
atoms[0].resid :0x96fc250
&(atoms[0].resid->ID) :0x96fc25c
**(atoms[0].resid->ID) :1**
atoms[1].resid :0x96fc250
&(atoms[1].resid->ID) :0x96fc25c
**(atoms[1].resid->ID) :1**
atoms[2].resid :0x96fc3ec
&(atoms[2].resid->ID) :0x96fc3f8
(atoms[2].resid->ID) :2
atoms[3].resid :0x96fc3ec
&(atoms[3].resid->ID) :0x96fc3f8
(atoms[3].resid->ID) :2
---------------------------------------
atoms[0].resid :0x96fc250
&(atoms[0].resid->ID) :0x96fc25c
**(atoms[0].resid->ID) :891301941**
atoms[1].resid :0x96fc250
&(atoms[1].resid->ID) :0x96fc25c
**(atoms[1].resid->ID) :891301941**
atoms[2].resid :0x96fc3ec
&(atoms[2].resid->ID) :0x96fc3f8
(atoms[2].resid->ID) :2
atoms[3].resid :0x96fc3ec
&(atoms[3].resid->ID) :0x96fc3f8
(atoms[3].resid->ID) :2
Here is the code. I don't really know what I did wrong
#define FT_GRO 1
using namespace std;
class residue{
public:
residue(){}
residue(const residue& r){atoms=r.atoms; ID=r.ID; name= r.name;}
residue(int n, string s) {name=s;ID=n;}
public:
vector<class atom*> atoms;
int ID;
string name;
atom& addAtom(atom& a) { atoms.push_back(&a); return a;}
};
class atom{
public:
atom(){}
atom(const atom& a){ID=a.ID,name=a.name,coord=a.coord,resid=a.resid ;}
atom(const int anum, const string aname, const point3D& p, residue& res){coord=p; name=aname; resid=&res; ID=anum;}
~atom(){}
public:
point3D coord;
int ID;
string name;
double distance(point3D& p) {return coord.distance(p);}
double distance(atom& p) {return coord.distance(p.coord);}
class residue* resid;
};
int main(){
vector<atom> atoms;
vector<residue> residues;
double box1,box2,box3,x,y,z;
char l[256];
int nr,na;
string sr,sa;
int lastResNum = -1;
string lastResName("");
int nAtomsIn=4;
for(int i =0; i<nAtomsIn;i++){
cin.getline(l,255);
istringstream ssatm(l,ios::in);
ssatm >> setw(5) >> nr >> setw(5) >> sr >> setw(5) >> sa >> setw(5) >>na >> setw(8) >> x >>setw(8) >> y >>setw(8) >> z;
if (lastResNum!=nr || sr!=lastResName){
residues.push_back(residue(nr,sr));
}
point3D p(x,y,z);
atoms.push_back( atom(na,sa,p,residues.back()) );
residues.back().addAtom(atoms.back());
cout << "atoms["<<i<<"].resid :" << atoms[i].resid << endl;
cout << "&(atoms["<<i<<"].resid->ID) :" << &(atoms[i].resid->ID) << endl;
cout << "&(atoms["<<i<<"].resid->ID) :" << (atoms[i].resid->ID) << endl;
lastResNum=nr; lastResName=sr;
}
cout << "---------------------------------------"<<endl;
cin.getline(l,255);
istringstream ssbox(l);
ssbox >> setw(10) >> box1>>setw(10) >> box2>>setw(10) >> box3;
for(int i =0; i<atoms.size();i++){
cout << "atoms["<<i<<"].resid :" << atoms[i].resid << endl;
cout << "&(atoms["<<i<<"].resid->ID) :" << &(atoms[i].resid->ID) << endl;
cout << "&(atoms["<<i<<"].resid->ID) :" << (atoms[i].resid->ID) << endl;
}
return 0;
}
What you're seeing is perfectly normal behaviour -- when you add new elements to a vector, it may get resized, hence all the elements are copied to a new memory location.
If you need a guarantee that existing elements aren't moved in memory, use a different container such as list or set.
std::vector will move memory when it needs more space. It allocates a contiguous block of memory, and when that block fills up, it allocates a bigger block, copies all the elements from the old block to the new one, frees the old block, and moves on.
To prevent the behavior you are seeing, you can do any of a few things to improve your design patter:
1) Change your vectors in main() to store pointers instead of stack options. This way, the object will always be in the same place in memory.
2) Change your class declarations to allow deep copies by implementing a copy-constructor and assignment operator
3) Modify your class heirarchy to remove the circular dependency between your classes. You can do this by having a Residue class, an Atom class, and another class that maps the 2 to each other.
The simplest option will be #1. You'll just need to make sure you clean up memory properly.
Like casablanca said, whenever your vector expands, it change where the objects are in memory.
If you really want to use vectors instead of some other container
1) you can reserve a large piece of memory for your vector. If you have a guarantee that the number of these objects won't exceed a certain bound and you don't mind using up that much memory, then make your vector that large.
2) make them vectors of pointers. If you have a very modern compiler (gcc >= 4.4 for example), you may even have access to the new unique_ptr smart pointer class from C++0x, which lets you use smart pointers in stl containers. These great additions to the language.
What i'm trying to do is create a template array class that will store values of a data type into an array. I have it working fine with int values, however working with string objects things start to break down.
I've taken out the block of code and tried it on it's own and I do get the same error. I'm sure I've learnt this, and I'm almost positive that the answer is something simple, trying to wrap my head around the pace in which we're learning c++ is a little crazy at times!
My best guess right now, is that I would need to tokenize the string and look for spaces. I tend to over think things though which lead to more confusion - thus me seeking out a answer here!
The code:
// Test String: Hello World this is a String Object
int stringSize = 7;
int count = 0;
string s[stringSize];
cout << "\nEnter " << stringSize << " one-word string values:\n";
while (count < stringSize) {
string tmpVal;
cin >> tmpVal;
s[count] = tmpVal;
count ++;
}
string s[stringSize]; is illegal because stringSize is not a constant. You must either use dynamic memory (i.e. string* s = new string [stringSize];), include stringsize as a template argument (don't do this, it doesn't actually solve the problem), use a fixed size value, or use an existing structure (I'd suggest vector, as in Bill's answer). The code below works fine on my compiler:
int main(int argc, char *argv[]) {
int stringSize = 7;
int count = 0;
string* s = new string [stringSize];
cout << "\nEnter " << stringSize << " one-word string values:\n";
while (count < stringSize) {
string tmpVal;
cin >> tmpVal;
s[count] = tmpVal;
count ++;
}
delete[] s;
}
I am a little confused as to exactly what you're looking for, but I suggest looking into the standard library.
Perhaps something like:
list<string> s;
and then, in the loop use push_back.
I am also confused what is your actual question, because your code works. However, FWIW, I would suggest the following. The changes are: (1) use of const (already suggested by others), (2) use of size_t, (3) change of variable name stringSize to numStrings (because of this I was confused at first glance), and (4) avoiding string copy.
#include <iostream>
#include <string>
using namespace std;
int main()
{
const size_t numStrings = 7;
size_t count = 0;
string s[ numStrings ];
cout << "\nEnter " << numStrings << " one-word string values:\n";
while (count < numStrings) {
cin >> s[ count ];
count++;
}
return 0;
}
Why not read in the entire line, then find all spaces and using the substr method, split the string?
You will need the following methods:
getline()
find_first_of()
substr()
Also, searching around this site for splitting strings in c++ will give you a lot of tips.
First of all, the size of your array should be constant:
const int stringSize = 7;
Secondly, as dbrien said, you should use std::vector unless you're doing this for the learning experience:
std::string tmpVal;
std::vector<std::string> s;
cout << "\nEnter " << stringSize << " one-word string values:\n";
while (cin >> tmpVal)
{
s.push_back(tmpVal);
}
First, the array dimension must be constant, so it should be const int stringsize = 7; Also, I would suggest using std::vector rather than std::list, additionally What was the error?
Not sure what error you're getting, but this is wrong because you need to use a constant integral value to allocate arrays on the stack.. Change:
int stringSize = 7;
int count = 0;
string s[stringSize];
... to:
const int stringSize = 7;
int count = 0;
string s[stringSize];
You can and probably should also use a vector instead of using C-style arrays, or trying to hand roll your own templated array class:
vector<string> s;
const int stringSize = 7;
cout << "\nEnter " << stringSize << " one-word string values:\n";
while (s.size() < stringSize) {
string tmpVal;
cin >> tmpVal;
s.push_back(tmpVal);
}
So it turns out it was the compiler. I was using xCode and getting:
cin_cout(7307) malloc: *** error for object 0x1000072c0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Running the same block in Visual c++ seemed to be ok... Sorry for my stupidity and thanks kindly for all the quick feedback!