Reputation: 7470
I have a function which returns a const char*
const char* SayHi()
{
string data="Mustafa Hi";
const char* cptr=data.c_str();
return cptr;
}
int main()
{
cout<<SayHi()<<endl;
return 0;
}
But output is not : Mustafa Hi. It is null.
If i define data variable as global like
string data;
const char* SayHi()
{
data="Mustafa Hi";
const char* cptr=data.c_str();
return cptr;
}
int main()
{
cout<<SayHi()<<endl;
return 0;
}
Output is Mustafa Hi.
But for const int* this works;
const int* aMethod()
{
int* aVar=new int(111);
const int* acstPtr=aVar;
return acstPtr;
}
int main()
{
cout<<*aMethod()<<endl;
return 0;
}
Output : 111
or
const int* aMethod()
{
int aVar =111; //in stack
const int* acstPtr=&aVar;
return acstPtr;
}
so why not null?
Upvotes: 1
Views: 5922
Reputation: 24249
Local variables are created on the "stack", which have a lifetime of the scope they exist in. In particular, what is happening in your original case:
string data="Mustafa Hi";
const char* cptr=data.c_str();
return cptr;
is complicated by the fact thatstd::string is an object with a destructor. Prior to C++11, it may have stored the string without the terminating zero unless you called c_str(). Internally, it contains a pointer and a size value and perhaps also a capacity value. It allocates memory to store the string. Think of (pre-C++11) std::string like this:
class String {
char* m_ptr;
size_t m_length;
size_t m_alloc;
public:
String() : m_ptr(nullptr), m_length(0), m_alloc(0) {}
String(const char* src) {
size_t length = strlen(src);
m_ptr = new char[length];
m_alloc = m_length;
memcpy(m_ptr, src, length); // not including the \0
}
const char* c_str() {
if (m_length == 0)
return nullptr;
if (m_alloc > m_length) // we're null terminated
return m_ptr;
char* newPtr = new char[length + 1];
memcpy(m_ptr, newPtr, length);
delete [] m_ptr;
m_ptr = newPtr;
m_ptr[length] = '\0';
++m_alloc;
return m_ptr;
}
~String() {
#ifdef _DEBUG
if (m_ptr) m_ptr[0] = 0;
#endif
delete [] m_ptr;
}
};
Your function is taking the address of an instance of this object, then returning that address. The next thing that happens is that the instance goes out of scope and calls it's destructor -- it was on the stack and immediately after your code, so the stack location it was in is now available for use by whatever code gets called next.
Take a look at the following sample (live demo: http://ideone.com/wAcY3B)
#include <iostream>
int* getInt1(int input)
{
int i = input;
std::cout << "created 'i' at " << (void*)&i << std::endl;
int* ptr1 = &i;
return ptr1;
}
int* getInt2(int input)
{
int* ptr3 = new int(input);
return ptr3;
}
int main()
{
int i = 0;
std::cout << "i is on the stack, it's address is " << (void*)&i << std::endl;
int* ip = new int(1);
std::cout << "ip is on the heap, it's address is " << (void*)ip << std::endl;
int* p1 = NULL;
int* p2 = NULL;
int* p3 = NULL;
// force the pointers to be assigned locations on the stack by printing them.
std::cout << "created p1(" << &p1 << "), p2(" << &p2 << ") and p3(" << &p3 << ")" << std::endl;
p1 = getInt1(10101);
std::cout << "p1(" << &p1 << ") = " << (void*)p1 << " -> " << *p1 << std::endl;
p2 = getInt1(20202);
std::cout << "p2(" << &p2 << ") = " << (void*)p2 << " -> " << *p2 << std::endl;
// but more importantly
std::cout << "p1(" << &p1 << ") = " << (void*)p1 << " -> " << *p1 << std::endl;
p3 = getInt2(30303);
std::cout << "p3(" << &p3 << ") = " << (void*)p3 << " -> " << *p3 << std::endl;
std::cout << "p2(" << &p2 << ") = " << (void*)p2 << " -> " << *p2 << std::endl;
std::cout << "p1(" << &p1 << ") = " << (void*)p1 << " -> " << *p1 << std::endl;
}
Output looks like this:
i is on the stack, it's address is 0xbfb49a90
ip is on the heap, it's address is 0x9b83008
created p1(0xbfb49a94), p2(0xbfb49a98) and p3(0xbfb49a9c)
created 'i' at 0xbfb49a6c
p1(0xbfb49a94) = 0xbfb49a6c -> 10101
created 'i' at 0xbfb49a6c
p2(0xbfb49a98) = 0xbfb49a6c -> 20202
p1(0xbfb49a94) = 0xbfb49a6c -> -1078682988
p3(0xbfb49a9c) = 0x9b83018 -> 30303
p2(0xbfb49a98) = 0xbfb49a6c -> -1078682988
p1(0xbfb49a94) = 0xbfb49a6c -> -1078682988
Because the stack pointer doesn't change between calls to "getInt1()" it's local variable instances are in the same location, but if you call some other random functions in-between assignments, they will use the same stack location and your pointed-to-values will be lost.
Upvotes: 1
Reputation: 396
But output is not : Mustafa Hi. It is null.
It could be anything. Issue is with returning the member variable (const char *) of a local object (string), which will be cleaned once we come out of the enclosing curly brace. Output is undefined in such cases. On few compilers you may even get the desired output if the the memory that was released was not reallocated.
It works for global variable because they span throughout the life time of the program and is not cleaned up after the control exits SayHi()
const int* this works;
This is dynamic memory allocation as against the stack allocation earlier. Unlike stack allocation (creating local object), with heap allocation (using new) you have the memory and the value until you delete the memory explicitly.
To replicate the first scenario with int, your code should be something like,
const int* aMethod()
{
int aVar=111; // changed from pointer to local variable. Heap to stack
const int* acstPtr= &aVar; //Returning the address for local variable. Which will be freed at the enclosing curly brace.
return acstPtr;
}
But again, output is undefined when you deal with pointers whose memory is already freed (dangling pointer)
Upvotes: 1
Reputation: 468
Try the code below. If you want to return a pointer to a local variable, you need to use a static keyword. All local variable will be free after it exist the function. That is the reason are not able to get the string properly return.
const char* SayHi()
{
static string data="Mustafa Hi";
const char* cptr=data.c_str();
return cptr;
}
Upvotes: 1
Reputation: 1838
You need to understand that "string data" inside of SayHi is a local variable. When the function exits local variables are destroyed.
To do what you're trying to do you either have to create the storage for the string so that it is not local to SayHi, either in main and pass it in or on the heap. Perhaps you'd like SayHi to return a reference.
Upvotes: 1
Reputation: 87959
Because in C++ objects have a lifetime. In your first example the string no longer exists once you exit the function. Therefore it's an error to return a pointer to that string's data.
Upvotes: 1
Reputation: 4408
The problem with your first implementation is that you return a pointer to temporary memory. Because the 'string data' variable is only defined within the scope of that function, when the function goes away, the pointer that you just returned is now pointing to free'd memory. In your case, it must be being overwritten with all zeros. That is why you are seeing NULL.
Upvotes: 8