tags:

views:

140

answers:

6
#include <iostream>

using namespace std;

class Marks
{
public:
    char* name();
};

char* Marks::name()
{
    char temp[30];
    cout<<"Enter a name:"<<endl;
    cin.getline(temp,30);
    return temp;
}

int main ()
{
    char *name;
    Marks test1;
    name=test1.name();

    //cout<<"name:"; //uncomment this line to see the problem
    cout<<name<<endl;

    return 0;
}
+2  A: 

You're returning a stack-based pointer (a pointer that resides in the stack of the called process) that will be released whenever the Marks::name() method ends.

This is basic C/C++ memory management question, so I encourage you to read some books on the topic.

There are several ways of doing this correctly. For example, reserving the memory for the string in the calling function and passing this pointer to the function:

In the main() method:

char name[30];
Marks test1;
test1.name(name);

(here name has the correct value) with the corresponding method:

char* Marks::name(char* temp)
{
    cout<<"Enter a name:"<<endl;
    cin.getline(temp,30);
    return temp;
}

But there are other ways of managing static and dynamic memory for strings.

Diego Sevilla
+5  A: 

The problem is because the value that name is pointing to has been destroyed. You are returning the address of a local variable from Marks::name(). Most likely a side affect of the first cout is causing the contents of name to be destroyed. You're probably just getting lucky when the first cout is commented out.

The correct way to do this is to allocate some memory, return that, and then destroy it when you're done:

char* Marks::name()
{
    char* temp = new char[30];
    cout<<"Enter a name:"<<endl;
    cin.getline(temp,30);
    return temp;
}

int main ()
{
    char *name;
    Marks test1;
    name=test1.name();

    cout<<"name:";
    cout<<name<<endl;

    delete[] name;

    return 0;
}

Don't forget to use delete[], rather than just delete, since otherwise only the first character would be deallocated.

Andy
Note, the callee allocates and the caller disposes -- this is bad design and a potential source of memory leaks. Think of the caller and callee to be contained in separate libraries.
dirkgently
That is a good point. I was just trying to keep the code sample small to get the point across.
Andy
+1  A: 

Well, you have a pretty nasty bug in Marks::name() where you return a pointer to a temporary variable. temp will be destroyed at the exit of the function as it's going out of scope, so you are returning a pointer to a variable that doesn't exist anymore.

Timo Geusch
+7  A: 

You are returning address of a local variable:

char temp[30];
// ...
return temp;

This is a strict no-no in C or C++. The moment you come out of Marks::name() your temp variable goes BOOM! You can no longer access it -- doing that invokes Undefined Behavior. Try this,

#include <string> // somewhere at the top
// ...
std::string Marks::name()
{
   std::string line;
   cout<<"Enter a name:"<<endl;
   std::getline(cout, line);
   return line;
}
dirkgently
Just remember that string may not always be available. Sometimes you just have to get down and dirty with char* 's.
xan
Can you cite an example? IMHO, managing input with getline() and a char buffer is not the easiest of tasks.
dirkgently
Games programming for one. Often not the case, but in that kind of low down application you may not have parts of the std or boost libraries available. I didn't mean this exact problem specifically, but I've faced similar problems with mem. allocating inside functions myself.
xan
@xan: In normal situation this is just NOT true. Under exceptional conditions it may be true but by the time a beginner reaches these situations they will understand more.
Martin York
@xan: Martin York more or less summed up my thoughts. One last thing: it is better not to get burnt by low-level memory management when you have the option -- especially when begining to program.
dirkgently
@xan: see my comments too, in the accpeted answer -- a design issue.
dirkgently
A: 

All the answers so far are correct, but some of the ways of implementing this could have been better. I know its a very simple test application, but consider the following:

char* Marks::name()
{
    char* temp = new char[30];
    cout<<"Enter a name:"<<endl;
    cin.getline(temp,30);
    return temp;
}

int main ()
{
    char *name;
    Marks test1;
    name=test1.name();

    cout<<"name:";
    cout<<name<<endl;

    delete[] name;

    return 0;
}

Here, it is the function name() which is allocating the memory, but it is not deleting it. This is an inconsistancy which can lead to problems, especially in a more complicated senario. It might be better to do this (assuming you know ahead of time how much space you are going to give people to write in...)

//this time, pass in some ALREADY allocated memory, and a length count.
void Marks::name(char *buf, int len)
{
    cout<<"Enter a name (max " << len <<" letters):"<<endl;
    cin.getline(buf,len); //read into the memory provided
}

int main ()
{
    //preallocate your memory so that the same code that allocates...
    char *name = new char[30];
    Marks test1;
    test1.name(name, 30);

    cout<<"name:";
    cout<<name<<endl;

    //... also deallocates it. This is perhaps cleaner in terms of responcibility.
    delete[] name;

    return 0;
}

In reality there is no right or wrong way to do this kind of thing. Sometimes a function has to allocate memory to use and return this for other applications to use, this is just an example where you should think about who is newing and try to make it consistant when coming to delete.

xan
+1  A: 

Marks::name() returns a pointer to a local variable. That memory will be freed when the function exits.

The reason you still get the right output as long as you don't call any functions in between your calls to Marks::name and cout::operator << is that no other allocation claims that memory area.

You could use std::string instead of a char* if you intend to return a non-static string by value.

Alternatively, you could pass the char buffer to the name function as a char* and also pass the buffer's size and have Marks::name fill that buffer (e.g. using strncpy or strncpy_s)

void Marks::name( char* buffer, int length )
{
  cout<<"Enter a name:"<<endl;
  cin.getline(buffer,length);
}

void main()
{
  char buffer[30];
  Marks::name( buffer, 30 );
}
+1 - that's what I was driving at, and allocating on the stack is much better as long as you know at compile time how big the buffer will be.
xan