Reputation: 9708
Given the sample program below, retlocal1 works while retlocal2 doesn't. I know the rule about not returning a reference or pointer to a local variable but I was wondering how it works.
When retlocal1 returns it copies its value to EAX? But EAX is a register with enough space to hold an integer? So how does EAX hold the entire copy of the std::string (which could of course be a long long string).
There must be something going on under the hood that I don't understand?
This example is C++, but I assume C works exactly in the same way?
#include <string>
std::string retlocal1() {
std::string s;
s.append(3, 'A');
return s;
}
std::string& retlocal2() {
std::string s;
s.append(3, 'A');
return s;
}
int main(int argc, char* argv[]){
std::string d = retlocal1();
std::string e = retlocal2();
return 0;
}
Upvotes: 5
Views: 935
Reputation: 27385
There must be something going on under the hood that I don't understand?
There is.
retlocal2
returns a reference to a local object, which is undefined behavior (that is, the object goes out of scope and is destroyed, then you return an invalid reference to the calling code).
retlocal1
returns a movable temporary object (an r-value reference).
You would need to ask more specific questions if you want a more precise answer (not sure what you don't understand :) ).
Upvotes: 0
Reputation: 254751
The calling convention will specify how to return values that are too large for a single register. Smallish types might be returned in multiple registers; large types by passing a "hidden" pointer argument to the function, specifying where the returned value should be placed.
If you want to know all the gory details, Wikipedia is a good starting point.
Upvotes: 6
Reputation: 208456
When retlocal1 returns it copies its value to EAX? But EAX is a register with enough space to hold an integer? So how does EAX hold the entire copy of the std::string (which could of course be a long long string).
This is not correct. You should check the ABI for your platform, but the most common approach is that the calling convention for functions returning large (larger than a register) objects transforms the function into a function that takes an implicit pointer to the returned object. The caller allocates the space for the std::string
, and the return statement is transformed into copy construction into that location:
// Transformed function (with no NRVO)
void retlocal(std::string *ret) {
std::string s; s.append(3, 'A');
new (ret) std::string(s);
return;
}
The compiler for that particular case will apply Named Return Value Optimization, which will remove the object s
and construct in place of the returned object, avoiding the copy:
void retlocal(std::string *ret) {
new (ret) std::string();
ret->append(3,'A');
return;
}
Upvotes: 2