How are values ​​copied when passed as arguments?

So when you pass arguments by value, the value (s) are copied into the function parameter, but how does that work? Are the parameters just declared as regular variables and assigned the values ​​passed as arguments? Like this:

int getx(int z, int x)
{
int a = z+x;
return a;
}

int main()
{
int q = 2;
int w = 55;
int xx = getx(w, 2);
return 0;
}

      

If so, why would you call this to copy the value? Isn't the parameter variable just the value of the variable x? What is copied then?

+3


source to share


3 answers


Short and hilarious answer: You should think of variables as boxes containing a toy. if the function takes a parameter by vaule and you call the function, you just tell the function which toy you have in your drawer. He goes and gets his toy, which is exactly the same as yours (but not your toy), and plays with it. So you don't care what it does with the toy inside the function, because it's not your real toy. When you follow the link, you are effectively providing the game with a toy from its own box, and it plays with your toy, not its own.

Longer answer: This means that when you call int xx = getx(w, 2);

in your main function inside your getx function, you are going to use a chunk of data with the same bits in it as the chunks of data you passed in. but they are not the same piece of data. it means it z

is just a copy of the information that was in w

when you called the function.

Suppose you wrote getx

like this (where z

passed by value)

int getx(int z, int x) {
    int a = z + x;
    z = z + 1;
    return a;
}

      

In this case, after calling this function inside the main ( getx(w, 2)

), the copy was w

included as 55, and w

displayed as 55

In contrast, if you had this:



int getx(int& z, int x) {
    int a = z + x;
    z = z + 1;
    return a;
}

      

and then called it the way you do in your main

int main()
{
    int q = 2;
    int w = 55;
    int xx = getx(w, 2);
    return 0;
}

      

In this case, it is z

passed by reference (note the use int&

instead int

). This means that you are not using a copy of the data w

, you will actually be using the real w

data internally getx

. so in this case in your main function after the call getx(w, 2)

w

(not the copy) it goes like 55, so it w

comes out like 56

PS . Passing by reference is generally bad practice in my opinion. You can't tell just by reading getx(w, 2)

what w

will look different from what it was.

+1


source


To understand how functions operate on their parameter list, either by passing by value or by reference, one of the things you should try to do is create or simulate a table that will represent the stack stack for each line of code that is executed and for each new code block or region you need a new stack frame table.

Check out this short program and diagram:

main.cpp

#include <iostream>

int someCalculationByValue( int a, int b ) {
   a *= 2 + b;
   return a;         
}

int someCalculationByReference( int& a, int& b ) {
   a *= 2 + b;
   return a;
}

int main() {

    int x = 3;
    int y = 4;

    std::cout << "Starting with x = " << x << " and y = " << y << std::endl;
    int ansValue = someCalculationByValue( x, y );
    std::cout << "After Calculation" << std::endl;
    std::cout << "x = " << x << " y = " << y << std::endl;
    std::cout << "ansValue = " << ansValue << std::endl << std::endl;

    std::cout << "Starting with x = " << x << " and y = " << y << std::endl;
    int ansReference = someCalculationByReference( x, y );
    std::cout << "After Calculation" << std::endl;
    std::cout << "x = " << x << " y = " << y << std::endl;
    std::cout << "ansReference = " << ansReference << std::endl << std::endl;

    return 0;
}

      


Table . I'll skip the calls std::cout

, showing only local variables and UDFs for simplicity.

// Stack Frame Staring With First line of execution in the main function

// Stack Frame Start - Visible Scope Resolution of Main
{x} - Type int : 4bytes on 32bit - Scope Visibility - main()
+--+--+--+--+    
|  |  |  |  |    // On 32bit system 4bytes of memory - each block is 1 byte
+--+--+--+--+

{y} - Type int : 4bytes on 32bit - Scope Visibility - main()
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+

{ansValue} - Type int : 4bytes on 32bit - Scope Visibility - main()
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+

{=} assignment or evaluation

    {someCalculationByValue} - Function Call - New Scope - New Stack Frame

    {return value} - Type int 4bytes on 32bit - Returns back to calling function
    In this case it returns back to main and flows into assignment which then
    gets stored into {ansValue}
    +--+--+--+--+
    |  |  |  |  |  // Normally Has no name look up but is expected to be
    +--+--+--+--+  // returned by this function when it goes out of scope

    {a} - Type int - 4bytes on 32bit system - Parameter List - Visibility
    +--+--+--+--+    is local to this function only - it copies the value
    |  |  |  |  |    that is already stored in the variable that was passed
    +--+--+--+--+    to it or by direct value

    {b} - Type int - 4bytes on 32bit system - Parameter List - Visibility
    +--+--+--+--+    is local to this function only - it copies the value
    |  |  |  |  |    that is already stored in the variable that was passed
    +--+--+--+--+    to it or by direct value

    {a} an L-Value followed by compound assignment 
    {*=} a compound assignment followed by arithmetic operation or expression
         R-Value {a} = {a} * (2 + {b})
    {return} Return statement - return back to caller in this case main() which
             flows into the previous assignment in main that stores this return 
             value in {ansValue}

// Scope Resolution is now back in main()
{ansReference} - Type int : 4bytes on 32bit - Scope Visilbity - main()
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+

{=} assignment or evaluation

    {someCalculationByReference} - Function Call - New Scope - New Stack Frame
    {return value} - Type int 4bytes on 32bit - Returns back to calling function
    In this case it returns back to main and flows into assignment which then
    gets stored into {ansReference}
    +--+--+--+--+
    |  |  |  |  |  // Normally Has no name look up but is expected to be
    +--+--+--+--+  // returned by this function when it goes out of scope

    // No Local Variables Declared - Uses the actual variables that are passed 
    // in by the caller as this does substitution from its declarative variables
    {a} - the actual variable passed in  followed by compound assignment 
    {*=} followed by arithmetic operation or expression {a} = {a} * (2 + {b})
         However since this is by reference the direct use of main variables 
         are used so this then becomes: {x} = {x} * (2 + {y})
    {return} - back to caller in this case main() which flows into the previous
               assignment in main that stores this return value in {ansReference}

// Scope Resolution is now back in main()

      


You can now make the actual function calls regarding what the compiler is doing under the hood for each of these function calls.




someCalculationByValue ()

x & y are passed by value from main local scope variables
x has value of 3
y has value of 4

// Since passing by value
a is assigned a value of what x has which is 3
b is assigned a value of what y has which is 4

The arithmetic compound assignment and expression with substitution
{a(3)} *= 2 + {b(4)}
{a(3)}  = {a(3)} * (2 + {b(4)})
{a}     = (18)
return {a(18)} -> 18 is returned back and saved into main {ansValue}

      

In the main function, after the calculation, we print x and y to the console x is still 3 and y is still 4; nothing has changed with x and y.


someCalculationByReference ()

x & y are passed by reference from main local scope variables
x has value of 3
y has value of 4

// Since passing by reference
a is replaced with x that has a value of 3
b is replaced with y that has a value of 4

The arithmetic compound assignment and expression with substitution
Since by reference this function has no local variables of (a & b) it 
uses direct substitution of the variables that are passed in by its caller:
in this case; the main function.
{x(3)} *= 2 + {y(4)}
{x(3)}  = {x(3)} * (2 + {y(4)})
{x}     = (18)
return {x(18)} -> 18 is returned back and saved into main {ansReference}

      

This time we are printing the main stack local variables x and y, but this time x is no longer 3, now it is 18, just like the return value, since it was changed inside this function as a reference, the same will happen with y since it is also a reference, but we did not modify it in the second function, so its value remains unchanged. And there you have it; the difference between the outputs of pass-by-value (copied) or pass-by-reference (direct substitution).

0


source


It's a little tricky to figure out what your "sample" code should look like. This is partly because the underlying semantics of parameter passing doesn't really represent the code that can be shown very well. These are all behind the scenes detail and cannot really be expressed in language in any way more explicit than usual.

I'm also not entirely sure about your brief explanation of what exactly your mental argument passing model is, so I don't know where to start clarifying it. So let's just start from the beginning.

When a function takes a parameter by value, the caller of that function creates a new copy of the passed object and passes it to the function. The function then consumes that copy, doing what it wants. When this function ends, this copy is effectively deleted. This causes the caller to have its original object.

When a function takes a parameter by reference, the caller of that function actually passes its own copy of the function object. The function then consumes that copy, doing what it wants. When this function ends, any changes it has made to the object are permanent, since they are the same object and these changes are reflected in the caller's site. In other words, there is no copy.

Passing by value is actually how things work in C. When you do:

void Function(int foo);

      

the parameter foo

is passed by value. Likewise, when you do:

void Function(int * foo);

      

the parameter foo

is still passed by value; it's just that the parameter passed by value is actually a pointer, so it simulates passing "by reference" as you are passing a reference to the original value in memory using a pointer.

In C ++, you do have real pass-by-reference semantics because the language has first-class reference types. So when you do:

void Function(int & foo);

      

the parameter is foo

actually passed by reference - Function

gets a reference to the original object that the caller has. Now, behind the scenes, C ++ is going to inject references through pointers, so nothing new really happens. You just get a guarantee from the language that a "null" link will never be created, which will save you from a whole category of errors.

I believe that one understanding of these details can be improved by looking at how this is actually implemented under the hood by the compiler. Implementation details differ in different implementations and architectures, but in general there are two main ways of passing parameters to functions: either onto the stack or into internal processor registers.

If the parameter is passed on the stack, the caller pushes the value onto the stack. Then the function is called and reads / consumes data from the stack. After the function is completed, the parameter will "pop" off the stack. In the language of pseudo-association:

PROCEDURE getx                    // int getx(int one, int two)
    LOAD    reg1, [stack_slot1]   // load parameter from stack slot #1 into reg1
    LOAD    reg2, [stack_slot2]   // load parameter from stack slot #2 into reg2
    ADD     reg1, reg2            // add parameters (reg1 += reg2)
    RETURN  reg1                  // return result, in reg1
END


PROCEDURE main                    // int main()
    PUSH   2                      // push parameter 1 onto stack
    PUSH   55                     // push parameter 2 onto stack
    CALL   getx                   // call function 'getx'

    // The function has returned its result in reg1, so we can use it
    // if we want, or ignore it.

    POP    stack_slot1            // pop parameters from stack to clean up stack
    POP    stack_slot2
    RETURN 0
END

      

Here we were "pushing" constant values ​​onto the stack. However, we could just as easily push a copy of the value into a register.

Note that "push" makes a copy of the value, so passing through the stack will always be passed by value, but as we said, a copy of a pointer can be passed to pass by -receptual semantics. Any changes made to the object through the pointer will be reflected in the called party.

If a parameter is passed in a register, the caller must ensure that the value is loaded into the appropriate register. Then the function is called and it reads / uses data from this register. After the function finishes, all changes made to the value in the register are still visible. For example:

PROCEDURE getx                    // int getx(int one, int two)
    ADD     reg1, reg2            // add parameters (reg1 += reg2)
    RETURN                        // result is left in reg1
END


PROCEDURE main                    // int main()
    MOVE   reg1, 2                // put '2' in reg1
    MOVE   reg2, 55               // put '55' in reg2
    CALL   getx                   // call function 'getx'

    // The function has modified one or both registers, so we can use
    // those values here, or ignore them.

    RETURN 0
END

      

If it main

does something else with values ​​before or after a function call, then it can do it in the same registers it getx

uses for its parameters. This will basically be the semantics of link traversal. Or it can get pass-by-value semantics by first copying the values ​​into new registers, calling getx

and then copying the result (s).

0


source







All Articles