-2

Right now I'm learning the ins and outs of C and C++. I know that when you create an array inside a function, then it is stored inside that function's stack frame. You can return the base address of the array, which is in fact a pointer to the first element in that array. That returned pointer value gets stored into the EAX/RAX register, and then the value from the register is then moved into a pointer variable local to the calling function. The problem is that when the function returns, that function's stack frame gets popped off the called stack, and any data declared inside that function's stack frame expires. The pointer is now pointing to an invalid memory location.

I want to be able to return an array from a called function BY VALUE, not by pointer. The array has to be created inside the function and stored on the stack. I want to return an array by value just as you would return an int that was declared inside the called function.

int f() {
   int a = 5;
   return a;  // returned by value
}

int main() {
    int b = f();
    return 0;
}

Here the int value is moved into the EAX/RAX register, so it is a copy. The called function's stack frame is cleared off the call stack, but there is no problem since the returned value is now stored in the register just before copying it into int b.

I know that in C++ I can create a vector inside the called function and then return it by value. But I don't want to use such higher level abstractions in favor of learning a "hacky" way to do it. I'll come back to vectors in a bit.

Well, I realized that it is possible to return a struct object by value from a function. So my solution to returning an array by value is very simple: put it inside a struct, and return that struct by value!

struct String {
    char array[20];
};

struct String f() {
    struct String myString;
    strcpy(myString.array, "Hello World");
    return myString;  // Is this returned by value?
}

int main() {
    struct String word = f();
    printf("%s\n", word.array);
}

Please clarify me if I understand the code correctly. That struct object gets created inside the called function's stack frame, "Hello World" is copied into the array contained within, and then what?

The struct String word is a lvalue and f() returns an rvlaue. When one struct is assigned to another all of it's data members are copied one by one.

What happens in between, just after the struct is returned from the called function by value, and before it is assigned to the struct inside the main() function? The EAX/RAX register is the destination for returned values. It is either 64 bits or 32 bits depending if you have a 64 or 32-bit computer. How exactly do you fit a struct object into a register? I imagine that the array maybe not only 20 bytes, but let's say 100 bytes! Is the struct copied from the function into the register piece-by-piece? Or is it copied from one memory location on the stack to another by value all in one go? And also what happens to the original struct object which was created inside the called function? Those are all questions that I'd like to know answers to.

Also, about returning vectors from functions by value. Vectors in C++ are classes, and classes are similar to structs. Can you answer the question, what happens when you return a vector by value from a function? And what happens when you pass a class/struct object into a function as a parameter?

I can imagine how pass by value works with small data types. I don't even know how it works for complex data types and data structures.

Galaxy
  • 2,363
  • 2
  • 25
  • 59
  • 3
    Is this one question or several? Please make sure to ask one specific question. – Yunnosch Jul 13 '18 at 05:13
  • I intended to ask one question, but I got too carried away! – Galaxy Jul 13 '18 at 05:14
  • 2
    Make it one, please. – Yunnosch Jul 13 '18 at 05:15
  • Are you specifying that you wish to return a pointer in your method? Please post the code you already tried running. – Robert Jul 13 '18 at 05:15
  • Please search for "return value optimization", "copy elision", "move constructor" etc. – taskinoor Jul 13 '18 at 05:15
  • 3
    C is not C++ is not C, choose only one language – Stargateur Jul 13 '18 at 05:20
  • Also these things are implementation defined. So the answer might well be different for different architecture. – taskinoor Jul 13 '18 at 05:23
  • "to return an array from a called function BY VALUE," --> Put it in a `struct` and return that. – chux - Reinstate Monica Jul 13 '18 at 05:36
  • Check [this](https://stackoverflow.com/questions/3473438/return-array-in-a-function). The same question . – Mister_Jesus Jul 13 '18 at 05:37
  • 2
    There's way to much going on here. Do you want to know if it's possible to return a struct in C? In C++ Do you want to know what the assembly code to do it is in C? in C++? In a particular compiler? Do you want to know how parameters are copied? Hoe C++ vectors are copied? Narrow it down. – Retired Ninja Jul 13 '18 at 05:40
  • There is a concept called Return Value Optimization supported by most of C++ compilers. While returning object of Structure or Class from a function, compiler creates a hidden object on stack and actual object is created in a memory outside stack. Hidden object address is mapped to this allocated object outside stack. You can get more information on below links - https://en.wikipedia.org/wiki/Copy_elision#Return_value_optimization https://stackoverflow.com/questions/753312/returning-large-objects-in-functions –  Jul 13 '18 at 06:06
  • Why downvote this question when it strongly connects with the name of this site? :) –  Jul 13 '18 at 06:13
  • See [this recent question](https://stackoverflow.com/questions/50808782/). – Steve Summit Jul 13 '18 at 09:28

1 Answers1

0

The precise mechanism is platform-dependent. But the most common mechanism is that the caller allocates space on its stack for the struct to be returned and passes the address of that space as an extra argument, usually before all the real arguments.

On many platforms, structs small enough to fit in a register will be returned as though they were a single value. This would apply on x86-64 for struct consisting of two 32-bit ints, since they could be returned in a single 64-bit register. How large a struct can be handled this way will vary from platform to platform.

The cost of passing larger structs by value can be ameliorated by copy elision. If, for example, you write

struct MyThingy blob = blobMaker();

the compiler is likely to pass blobMaker the address of the variable blob rather than allocating a temporary variable and then copying the temporary to blob after the function returns. The called function may also be able to avoid copies:

struct MyThingy blobMaker(void) {
  struct MyThingy retval;
  // ...
  retval.member1 = some_calc(42);
  // ...
  retval.member2 = "Hello";
  // ...
  return retval;

Here, the compiler might chose to not allocate retval in the called function's stack frame, but instead just use the storage passed in the invisible argument directly, thus avoiding a copy at the return. The combination of these two optimisations (when possible) makes returning structs almost free.

The C++ standard provides for these optimisations by explicitly allowing them even in cases where the elided copies might have triggered side effects in the object's copy constructor. (Obviously this case doesn't exist in C.)

rici
  • 234,347
  • 28
  • 237
  • 341