Overview
We discuss C++ value passing and returning, the sizes of objects, and recap some information on segments.
Modifying an argument
def f(arg):
    arg = -1
x = 2
f(x)
print("x is {}".format(x))
- We pass an integer variable, 
x, as an argument to a function - The function modifies its parameter
 - Does that modify the caller’s value?
- No!
 - The function parameter is a copy of the caller’s value
 - This is called call-by-value
 
 
Modifying a complex argument
def f(arg):
    arg = [-1]
l = [2]
f(l)
print("l[0] is {}".format(l[0]))
- Does modifying 
argaffect the caller? - Again no!
 
Modifying a complex argument’s value
def f(arg):
    arg[0] = -1
l = [2]
f(l)
print("l[0] is {}".format(l[0]))
- Does modifying 
arg’s value affect the caller? - YES!
- Python is not actually a call-by-value language
 - It is a call-by-object language (like Javascript, Java, …)
 - Simple objects are passed by value (int, bool, …)
 - Complex objects are effectively passed by pointer
 - Modifications to the parameter are not visible in the caller
 - But modifications to the parameter’s value are visible
 
 
C++
- C++ is a call-by-value language
 
C++ modifying an argument
void f(int arg) {
    arg = -1;
}
int main() {
    int x = 2;
    f(x);
    printf("x is %d\n", x);
}
C++ modifying a complex argument
void f(std::vector<int> arg) {
    arg = {-1};
}
int main() {
    std::vector<int> l = {2};
    f(l);
    printf("l[0] is %d\n", l[0]);
}
C++ modifying a complex argument’s value
void f(std::vector<int> arg) {
    arg[0] = -1;
}
int main() {
    std::vector<int> l = {2};
    f(l);
    printf("l[0] is %d\n", l[0]);
}
- The caller’s value does not change!
 - C++ arguments are passed by value regardless of type
- This can require copying an enormous data structure!
 - Try making 
lbigger and turning off optimization to see the effect 
 
Call-by-reference
- To avoid copying a data structure, pass it by reference using an explicit reference type
 
void f(std::vector<int>& arg) {    // `&` means reference
    arg[0] = -1;
}
int main() {
    std::vector<int> l = {2};
    f(l);
    printf("l[0] is %d\n", l[0]);
}
- Now all modifications to the parameter are visible to the caller
 - The parameter 
argbecomes an alias (a different name) for the caller’s variablel 
Every declared object has constant size
- The compiler needs to know the size of every object in order to lay it out in memory
 - Every declared variable has constant size
- For instance, array bounds must be constant
 - Can’t say 
int n = ...; int a[n]; 
 - Only dynamic allocation can create dynamically-sized arrays
 
How does std::vector work?
- What is 
sizeof(std::vector<int>)? 
Array parameters and return values are special
- C++ array parameters and return values are not passed by value
 - They are passed as pointers
 
void f(int arg[]) {
    arg[0] = -1;
}
int main() {
    int l[2] = {2, 0};
    f(l);
    printf("l[0] is %d\n", l[0]);
}
- This is surprising!
- We do not recommend passing normal arrays as arguments or return values
 - Pass pointers instead
 
 - Maybe C++ is bad (it is)
 - But everything in computers is weird until you get used to it
  
 
Stack segment growth
int f1(int f1arg) {
    hexdump_named_object(f1arg);
    return f1arg;
}
int f2(int f2arg) {
    hexdump_named_object(f2arg);
    return f1(rand()) + f2arg;
}
...
int f5(int f5arg) {
    hexdump_named_object(f5arg);
    return f4(rand()) + f5arg;
}
f5callsf4, which callsf3, which callsf2, which callsf1
Recursive call
- A recursive function is a function that can call itself
 
int f(int x) {
    int r = 0;
    ...
    if (x > 0) {
        r += f(x - 1);
    }
    ...
    return r;
}
int main() {
    f(100);
}
Stack layout vs. struct layout
- Struct layout is fixed by the abstract machine, to facilitate
interoperability with hardware and between different compilers
- If you want a tighter layout, you’ve got to do it yourself
 
 - Local variable layout has no such constraint!