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.
Related
I was trying to brush up my c++ skills.
I got 2 functions:
concat_HeapVal() returns the output heap variable by value
concat_HeapRef() returns the output heap variable by reference
When main() runs it will be on stack,s1 and s2 will be on stack,
I pass the value by ref only and in each of the below functions, I create a variable on heap and concat them.
When concat_HeapVal() is called it returns me the correct output.
When concat_HeapRef() is called it returns me some memory address (wrong output). Why?
I use new operator in both the functions. Hence it allocates on heap.
So when I return by reference, heap will still be VALID even when my main() stack memory goes out of scope.
So it's left to OS to cleanup the memory. Right?
string& concat_HeapRef(const string& s1, const string& s2)
{
string *temp = new string();
temp->append(s1);
temp->append(s2);
return *temp;
}
string* concat_HeapVal(const string& s1, const string& s2)
{
string *temp = new string();
temp->append(s1);
temp->append(s2);
return temp;
}
int main()
{
string s1,s2;
string heapOPRef;
string *heapOPVal;
cout<<"String Conact Experimentations\n";
cout<<"Enter s-1 : ";
cin>>s1;
cout<<"Enter s-2 : ";
cin>>s2;
heapOPRef = concat_HeapRef(s1,s2);
heapOPVal = concat_HeapVal(s1,s2);
cout<<heapOPRef<<" "<<heapOPVal<<" "<<endl;
return -9;
}
Both your functions are legal and fine, apart from that you're later leaking the free store objects from both functions.
The first function returns a reference to the value on the free store, which is copied into your local variable in main.
The second function returns a pointer to the instance on the free store, which is stored into a pointer variable in main.
Your problem is in that you do not print the second string, you print the address of it, thanks to that there's an operator<< that prints the address a pointer contains as a number.
If you intend to access the contents of the second variable, you should dereference it when using it.
When you return by reference, you signify (among other things) that the caller does not become owner of the data and is not responsible for freeing it.
You've allocated new block and the only remaining reference to it is the one you returned. So it cannot be by reference.
But it does not apply to all heap-allocated blocks. If you have heap-allocated block to which you hold pointer in member variable to be released later e.g. in destructor, you may and often will return it by reference.
But really, pointer to std::string is a code smell. std::string should most of the time should be returned by value. That is as string, string * is by pointer, not by value.
So it's left to OS to cleanup the memory. Right?
Right.
You leak the memory.
Your OS will have to reclaim the memory itself when your process ends, or it'll remain allocated until power down.
Please don't do this.
Stop using new immediately: just create a friendly new std::string object and return it. Let the compiler's optimisations and/or move semantics sort everything out.
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;
}
CARD& STACK::peek()
{
if(cards.size == 0)
{
CARD temp = CARD {-1, -1};
return temp;
}
return cards.back();
}
This is the function I am having trouble with.
CARD is just a struct with two int variables, called rank and suit.
STACK is a class that manages an std::vector<CARD>, that is called cards.
The function is supposed to return a reference to the card on top of the stack, or return the reference to a dummy card if the vector is empty.
First of all, I get a warning that says a reference to a local variable temp is returned. What is wrong with that? How will that effect the function? What do I do about it?
Second, I am trying to use this function with another function I created called cardToString
char* cardToString(CARD& c);
It is supposed to use the rank and suit variables in the passed CARD to look up string values in a table, concatenate the two strings together, and return a pointer to the new string.
So the end result looks like:
cout<<cardToString(deck.peek())<<"\n";
but this line of code will execute up to the cardToString function, then just stop for some reason. It is annoying the hell out of me because it just stops, there is no error message and there does not look like there is anything wrong to me.
Can somebody help me out?
Edit: here is the cardToString function
char *cardToString(const CARD& c)
{
if(c.r >= 13 || c.r < 0 || c.s >= 4 || c.s < 0)
{
std::cout<<"returned null";
return NULL;
}
char *buffer = new char[32];
strcpy(buffer, RANKS[c.r]);
strcat(buffer, " of ");
return strcat(buffer, SUITS[c.s]);
}
I specifically want the function STACK.peek() to return the address of the CARD that already exists on the top of the STACK. It seems to make more sense to do that than to create a copy of the card that I want to return.
First of all, I get a warning that says a reference to a local variable temp is returned. What is wrong with that? How will that effect the function? What do i do about it?
A local variable, as it name implies, is local to the function it belongs to, so it's destroyed as the function returns; if you try to return a reference to it, you'll return a reference to something that will cease to exist at the very moment the function returns.
Although in some cases this may seem to work anyway, you're just being lucky because the stack hasn't been overwritten, just call some other function and you'll notice it will stop working.
You have two choices: first of all, you can return the CARD by value instead of reference; this, however, has the drawback of not allowing the caller to use the reference to modify the CARD as is stored in the vector (this may or may not be desirable).
Another approach is to have a static dummy CARD instance stored in the STACK class, that won't have these lifetime problems, and that can be returned when you don't have elements in the vector; however, you should find a method to "protect" its field, otherwise a "stupid" caller may change the values of your "singleton" dummy element, screwing up the logic of the class. A possibility is to change CARD in a class that will encapsulate its fields, and will deny write access to them if it's the dummy element.
As for the cardToString function, you're probably doing something wrong with the strings (and I'm almost sure you're trying to return a local also in this case), but without seeing the body of the function it's difficult to tell what.
By the way, to avoid many problems with strings I suggest you to use, instead of char *, the std::string class, which takes away most of the ugliness and of the low level memory management of the usual char *.
Also, I'd suggest you to change cardToString to take a const reference, because most probably it doesn't need to change the object passed as reference, and it's good practice to clearly mark this fact (the compiler will warn you if you try to change such reference).
Edit
The cardToString function should be working fine, as long as the RANKS and SUITS arrays are ok. But, if you used that function like you wrote, you're leaking memory, since for each call to cardToString you make an allocation with new that is never freed with delete; thus, you are losing 32 bytes of memory per call.
As stated before, my tip is to just use std::string and forget about these problems; your function becomes as simple as this:
std::string cardToString(const CARD& c)
{
if(c.r >= 13 || c.r < 0 || c.s >= 4 || c.s < 0)
return "(invalid card)";
return std::string(RANKS[c.r]) + " of " + SUITS[c.s];
}
And you don't need to worry about memory leaks and memory allocations anymore.
For the reference/value thing: if the caller do not need to use the reference to modify the object stored in the vector, I strongly suggest passing it by value. The performance hit is negligible: two ints instead of one pointer means 8 vs 4 bytes on most 32 bit architectures, and 8 bytes vs 8 bytes on most 64 bit machines (and also accessing the fields via pointer has a small cost).
This kind of micro-optimization should be the last of your concerns. Your top priority is to write correct and working code, and the last thing you should do is to let micro-optimization get in the way of this aim.
Then, if you experience performance problems, you'll profile your application to find where the bottlenecks are and optimize those critical points.
You cannot return a reference to a local variable, because the local variable no longer exists when the function returns.
You need to return by-value, not by-reference (i.e. CARD STACK::peek() { ... }).
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.