In C and C++, parameter passing defaults to pass by value.
Unless you specify otherwise,
function parameters are initialized with copies of the actual arguments,
and function callers get back a copy of the value returned by the function.
Pass by value is the simplest way to get data into and out of functions.
voidprintFavorite(intx){// 'x' is a copy of 'favorite'std::cout<<"my favorite number is "<<x<<'\n';}intmain(){intfavorite=72;printFavorite(favorite);}
The important point is that two copies of my favorite number are stored.
The one declared in main, favorite, and
the one declared in printFavorite, x.
The parameter x is initialized using the value of favorite in main.
1#include<iostream> 2 3// define the function 4voidprintFavorite(intx){ 5std::cout<<"my favorite number is "<<x<<'\n'; 6} 7 8intmain(){ 9intfavorite=72;10printFavorite(favorite);11return0;12}
More than one parameter can be passed.
For example,
a function to add two numbers makes copies of both parameters
adds them together and returns a result, which is also copied.
Step through this example and see how the copies of both local variables and
return values are managed on the stack.
1#include<iostream> 2 3// This function takes two parameters. 4intadd_numbers(intx,inty){ 5intanswer=x+y; 6returnanswer; 7} 8 9intmain(){10inta=13;11intb=21;12intsum=add_numbers(a,b);13std::cout<<sum<<'\n';14return0;15}
One benefit of pass by value is that local changes to parameters
do not impact the caller.
That is, the caller can trust their data has not been modified.
For large / complex data types, however, pass by value becomes expensive even in small programs.
An alternative to pass by value, is called pass by reference.
The function parameter passed into the function is still a new variable.
That does not change.
However, rather than passing a copy of the entire object,
instead we bind the address of the original object to a new variable.
Only the object reference is passed to the function.
Pointers and References
In this respect, a reference behaves much like a constpointer.
Both require an initial value in order to compile
Neither can refer to (or point to) a different object
intn=3;// typical int declarationintm=5;int&a=n;// a refers to nint*p=&n;// p points to n,// but could point to something elseint*constp2=&m;// p2 point to m and can only point there
The following are compile errors:
int&b;// a reference that doesn't refer to anythingp2=&n;// attempt to change what a const pointer points to
If the pointer comparison is confusing, do not worry.
We will delve more deeply into pointers soon,
this is just for comparison for those people who have an introduction
into pointers.
Run It
1#include<iostream> 2 3intmain(){ 4intn=3;// typical int declaration 5intm=5; 6 7int&a=n;// a refers to n 8int&b=m;// b refers to m 910int*p=&n;// p points to n,11// but could point to something else1213int*constp2=&m;// p2 points to m and can only point there141516// any use of a is equivalent to thing the variable17// that a refers to18a=b;19std::cout<<"n = "<<n<<'\n';2021a=0;2223p=&m;// OK24//p2 = &n; // compile error2526std::cout<<"n = "<<n<<'\n'27<<"m = "<<m<<'\n'28<<"a = "<<a<<'\n'29<<"b = "<<b<<'\n'30<<"p = "<<*p<<'\n'31<<"p2 = "<<*p2<<'\n';32}
We use the address of operator& to declare that only the address of the
variable is passed, rather than a copy.
The primary advantage is that since all addresses are the same size,
the cost of passing is the same,
regardless of how large the object is.
Understanding references is critical to understanding how C++11 and
later version of the language function.
References are a major new language feature and we will be using them
often from now on.
Try This!
Modify the print_n function signature so that
the variable repeat is a reference instead of a copy.
1#include<cassert> 2#include<iostream> 3#include<string> 4 5voidprint_n(conststd::stringmessage,intrepeat){ 6while(repeat>0){ 7std::cout<<message<<'\n'; 8--repeat; 9}10}111213intmain(){14intn=3;15print_n("hello, world",n);16std::cout<<"n = "<<n;1718assert(n==0);// program terminates if false19}
A common source of confusion when starting out with references is
keeping the operator& straight.
The meaning of this operator depends on how it is used.
On the left-hand side of an assignment,
or in function parameters, &always defines a
reference to a type:
int&a=3;constint&cr(a);// cr refers to a,// but we can't change the value of a using crvoidshow_usage(std::string&message);constdouble&pi=3.1415926;
On the right-hand side of an assignment,
&almost always means address of a variable.
The only exception is when casting to a reference type:
intn=3;int*p=&n;// p points to the address of the variable nconstint&cr(a);// const reference// cast away the 'const' part of crint&r2=const_cast<int&>(cr);
In the last code block, notice that both cr and r2 refer to a,
however, r2 can change the value of a because we cast away the const
modifier that was part of cr.
Although the language allows casting away const like this,
you should use this feature very sparingly.
There is a lot going on in the following program.
You should step through this code and make sure you
understand what is happening to the variables in main
and the functions called from main.
1#include<iostream> 2 3// A copy of x is passed to this function. 4// Changes to x are not reflected in the caller. 5voidby_value(intx){ 6std::cout<<"in by_val the address of x is "<<&x<<'\n'; 7x=99; 8} 910// A reference to x is passed to this function.11// Changes to x are not reflected in the caller.12voidby_reference(int&x){13std::cout<<"in by_ref the address of x is "<<&x<<'\n';14x=-1;15}1617intmain(){18autoalpha=11;19autobeta=11;2021std::cout<<"in main the address of alpha is "<<&alpha<<'\n';22std::cout<<"in main the address of beta is "<<&beta<<'\n';2324by_value(alpha);25by_reference(beta);2627std::cout<<"alpha is now "<<alpha<<'\n';28std::cout<<"beta is now "<<beta<<'\n';29return0;30}