This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 6 years ago.
Consider the following code where I am returning double& and a string&. It works fine in the case of a double but not in the case of a string. Why does the behavior differ?
In both cases the compiler does not even throw the Warning: returning address of local variable or temporary as I am returning a reference.
#include <iostream>
#include <string>
using namespace std;
double &getDouble(){
double h = 46.5;
double &refD = h;
return refD;
}
string &getString(){
string str = "Devil Jin";
string &refStr = str;
return refStr;
}
int main(){
double d = getDouble();
cout << "Double = " << d << endl;
string str = getString();
cout << "String = " << str.c_str() << endl;
return 0;
}
Output:
$ ./a.exe
Double = 46.5
String =
You should never return a reference to a local variable no matter what the compiler does or does not do. The compiler may be fooled easily. you should not base the correctness of your code on some warning which may not have fired.
The reason it didn't fire here is probably that you're not literally returning a reference to a local variable, you are returning a variable that is a reference to a local variable. The compiler probably doesn't detect this somewhat more complex situation. It only detects things like:
string &getString(){
string str = "Devil Jin";
return str;
}
The case of the double is simpler because it doesn't involve constructing and destructing a complex object so in this situation the flow control analysis of the compiler probably did a better job at detecting the mistake.
The reference to a double refers to a location that is still physically in memory but no longer on the stack. You're only getting away with it because the memory hasn't been overwritten yet. Whereas the double is a primitive, the string is an object and has a destructor that may be clearing the internal string to a zero length when it falls out of scope. The fact that you aren't getting garbage from your call to c_str() seems to support that.
GCC used to have an extension called Named Returns that let you accomplish the same thing, but allocated the space outside the function. Unfortunately it doesn't exist anymore; I'm not sure why they took it out
Classic case of a Dangling reference in respect to C++.The double variable was not on call stack while returning reference was trying to access it invoking the compiler to set the guarding flags. String however has explicit Garbage Collection mechanism which lets your compiler to overlook the scenario.
Related
Currently I am learning C++ with 6th edition C++ Primer Plus by Steven Prata and on p. 389 I found smth very interesting that I would like to clarify for myself.
If we have some function like this:
const std::string &ft_add_on_sides(std::string s1, std::string s2)
{
std::string res;
res = s2 + s1 + s2;
return (s2);
}
int main(void)
{
std::string input = "Hello"
std::string result = ft_add_on_sides(input, "###");
return (0);
}
As far as I understand, this function is expecting two string objects which will be copies of those, which programmer will actually pass. And this function will return const reference to some memory address.
But I am interested in return statement:
As s1 or s2 are located in a temporary storage, we can gain access to them, but when will they be destroyed? Won't I get into troubles with such return statement?
Add. Cause the variable can be destroyed and I am still asking for its address.
Parameters of a function are destroyed as soon as the return statement has executed, before the calling function continues. That means you can still use these parameters inside the return value expression, but you'd better return a copy of them.
What you wrote in your example is Undefined Behavior. Anything can happen. Typically, in debug modes compilers are reasonably good in catching accesses to destroyed strings, but in release builds the compiler no longer introduces code to catch bugs.
You can read the accepted answer here.
Return value of function
the return value is copied out of a local variable into the return register if necessary.
The functions' stack frame is no longer needed, the local variables are popped off and will not be accessible once the function returns, but s2 will be copied into the return register.
This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 6 years ago.
void changeString(const char* &s){
std::string str(s);
str.replace(0, 5, "Howdy");
s = str.c_str();
}
int main() {
const char *s = "Hello, world!";
changeString(s);
std::cout << s << "\n";
return 0;
}
When I run this code, it prints "Howdy, world!" I would think that str gets destroyed when changeString exits. Am I missing something with the way std::string gets allocated?
Yes, str is destroyed; but the memory of the string isn't cleared; your "s" pointer point to a free but non cleared memory. Very dangerous.
It's undefined behaviour when std::cout << s tries to access the pointer, because the destructor of the local std::string in changeString has freed the memory which the pointer still points to.
Your compiler is not required to diagnose the error but can instead generate a binary which can then do whatever it wants to.
The fact that you got the desired output was just bad luck because it made you think that your code was correct. For instance, I've just compiled your code on my machine and got empty output instead. It could also have crashed, or it may have done other, unrelated things.
This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 8 years ago.
Please consider this simple example:
#include <iostream>
const int CALLS_N = 3;
int * hackPointer;
void test()
{
static int callCounter = 0;
int local = callCounter++;
hackPointer = &local;
}
int main()
{
for(int i = 0; i < CALLS_N; i++)
{
test();
std::cout << *hackPointer << "(" << hackPointer << ")";
std::cout << *hackPointer << "(" << hackPointer << ")";
std::cout << std::endl;
}
}
The output (VS2010, MinGW without optimization) has the same structure:
0(X) Y(X)
1(X) Y(X)
2(X) Y(X)
...
[CALLS_N](X) Y(X)
where X - some address in memory, Y - some rubbish number.
What is done here is the case of undefined behaviour. However I want to understand why there is such behaviour in current conditions (and it is rather stable for two compilers).
It seems that after test() call first read of hackPointer leads to valid memory, but second successive instant read of it leads to rubbish. Also on any call address of local is the same. I always thought that memory for stack variable is allocated on every function call and is released after return but I can't explain output of the program from this point of view.
"Releasing" automatic storage doesn't make the memory go away, or change the pattern of bits stored there. It just makes it available for reuse, and causes undefined behaviour if you try to access the object that used to be there.
Immediately after returning from the function, the memory occupied by the local probably hasn't been overwritten, so reading it will probably give the value that was assigned within the function.
After calling another function (in this case, operator<<()), the memory is likely to have been reused for a variable within that function, so probably has a different value.
You are quite right that this is undefined behaviour.
That aside, what's happening is that std::cout << *hackPointer involves a function call: operator<<() gets called after the value of *hackPointer has been read. In all likelihood, operator<<() uses its own local variables that end up on the stack where local was, wiping out the latter.
I am currently working my way through C++ Primer Fifth Edition. I have gone through a couple of other C++ books, but they weren't very detailed and were quite complicated.
This book has been helping me a lot with everything that I have missed. I've just hit a wall.
One of the exercises asks me to write a declaration for a function that returns a reference to an array of ten strings, without using trailing return, decltype, or type alias.
I know it only says write a declaration, which I have done, like so:
string (&returnArray()) [10];
I wanted to write a function definition as well, like so:
string (&returnString(int i, string s)) [10]
{
string s1[10];
s1[i] = s;
return s1;
}
In my main function, I have a for loop which passes a string literal through and stores that string inside a pointer to an array of ten strings. It should then output the results to the screen.
The problem I am having is, when I dereference my pointer to an array, once, it will output the address. If I dereference it twice, the program outputs nothing and stops responding.
Here is my main function, I have changed it multiple times, yet can't figure out why it's not outputting properly. I've probably got it all wrong...
int main()
{
string (*s)[10];
for(int i = 0; i != 10; ++i)
{
s = &returnString(i, "Hello");
cout << s[i] << endl;
}
return 0;
}
Your function returns a reference to a local variable – after the call, it’s a dangling reference. You cannot do that.
You can only return references to storage that goes on existing after the end of the function call.
Returning a reference to a temporary local object invokes undefined behavior.
A short fix is making it static:
string (&returnString(int i, string s)) [10]
{
static string s1[10];
^^^^^^
s1[i] = s;
return s1;
}
int main()
{
string(&s)[10] = returnString(0, "Hello");
for (int i = 0; i != 10; ++i)
{
s[i] = "Hello";
cout << s[i] << endl;
}
}
Returning pointers/references to local variables is bad, undefined behavior. You can follow your exercise and not forget that constraint, your exercise tells you to do something but it's not necessarily telling you to do it the wrong way, in fact it's a good one that will lead you to pitfalls and hence make you a better programmer once you figure them out.
So what's left given that constraint of not returning addresses to local variables but still addressing the task given? You have the static fix as M M. already mentioned, or you could think you're creating something useful like a function rotate, for example, that accepts an string(&)[10] and returns itself rotated ;-)
Look that, iostream insertion and extraction operators already work like that, returning references to parameters passed by reference.
Why can this code run successfully in Code::block. The IDB just reports
warning: "reference to local variable ‘tmp’ returned",
but ouput the result "hello world" successfully.
#include <iostream>
#include<string>
using namespace std;
const string &getString(const string &s)
{
string tmp = s;
return tmp;
}
int main()
{
string a;
cout<<getString("hello world")<<endl;
return 0;
}
Upon leaving a function, all local variables are destroyed. By returning a reference to tmp, you are returning a reference to an object that soon ceases to exist (that is, technically, the address of a memory region whose contents are no longer meaningful).
Accessing such a dangling reference invokes what is called 'undefined behaviour' - and sadly, 'work as usual' is one kind of 'undefined behaviour'. What might happen here is that std::string keeps a small static buffer for small strings (as opposed to large strings, for which it grabs memory from the heap), and upon leaving getString the stack space occupied by this string is not zeroed so it still seems to work.
If you try a debug build, or invoke another function in between (which will effectively overwrite the stack space), it won't work anymore.
You are causing an undefined behaviour. The standard doesn't tell what happens in that case, however your compiler detected it.
tmp disappears the moment you return from getString. Using the returned reference is undefined behaviour, so anything can happen.
To fix your code, return the string by value:
string getString(const string &s)
{
...
Are you sure? It should segfault (it will with gcc on most platforms). That code does contain an error, and if you get 'lucky' and it works, then you're brushing a nasty bug under the carpet.
Your string is returned by reference, that is, rather than making a new string which is valid in the context you are returning into, a pointer to a stale, destructed, object is being returned, which is bad. const string getString... will do as the declaration for the function's return type.
The temporary object is deallocated, however its contents are still there on the stack, until something rewrites it. Try to call a few functions between calling your function and printing out the returned object:
const string& garbage = getString("Hello World!");
callSomeFunctionThatUsesALotOfStackMemory();
cout<< garbage << endl;
As you can see the below example code is just slightly modified by calling goodByeString(). Like the other answers already pointed out the variable in getString called tmp is local. the variable gets out of scope as soon as the function returns. since it is stack allocated the memory is still valid when the function returns, but as soon as the stack grows again this portion of memory where tmp resided gets rewritten with something else. Then the reference to a contains garbage.
However if you decide to output b since it isn't returned by reference the contents is still valid.
#include <iostream>
#include<string>
using namespace std;
const string &getString(const string &s)
{
string tmp = s;
return tmp;
}
string goodByeString(const string &s)
{
string tmp = "lala";
tmp += s;
return tmp;
}
int main()
{
const string &a = getString("Hello World!\n");
string b = goodByeString("ciao\n");
cout << a << endl;
return 0;
}