C/C++ Call by Reference in C++: I'm Confused!

  • Thread starter Thread starter Edgardo
  • Start date Start date
  • Tags Tags
    Reference
AI Thread Summary
The discussion centers on the confusion surrounding call by reference in C++, specifically the differences between using references and pointers. It clarifies that a reference, such as in "void squareCBR(int& x)", acts as an alias for the variable passed, allowing direct manipulation without needing to pass an address. Conversely, pointers require explicit address passing, as shown in "void squareCBR2(int* x)", which can lead to confusion due to the need for dereferencing. Participants highlight that while references simplify syntax and avoid null pointer issues, they cannot be reassigned like pointers, leading to different use cases. Overall, understanding these distinctions is crucial for effective C++ programming.
Edgardo
Messages
706
Reaction score
17
I am confused by two versions of call by reference in C++. As an example two methods that square an integer (CBR stands for Call By Reference):

Code:
//Version 1
void squareCBR(int& x)
{
     x = x * x;
}

//Version 2
void squareCBR2(int* x)
{
     (*x) = (*x) * (*x);
}

Let's call the functions:

Code:
int x = 3;
squareCBR(x);
squareCBR2(&x);

1) What I don't understand is that I'm calling squareCBR(int& a)
by writing squareCBR(a). Why just a? I thought I'd have to pass an address to the function since the argument is (int& a).

2) The same question about squareCBR2(int* x). I call this function by writing squareCBR2(&x). Why &x? I thought I'd have to pass a pointer since the argument is (int* x).
 
Last edited:
Technology news on Phys.org
&a is the address-of operator, and it returns a pointer to a.

If "a" has type "int" then "&a" has type "int*", like this

Code:
int a; //new variable a
int *x = &a; //x is a pointer to a

*x is the dereference operator, and returns the value pointed to by the pointer x. If "x" has type "int*" then "*x" has type "int", like this

Code:
int b = *x; //b is a new variable assigned value pointed to by x (ie, a)

When you pass an argument as a copy, it looks like this:

Code:
int funcByCopy(MyClass x)
{
   //stuff
}

and it is called like this:

Code:
MyClass x;
funcByCopy(x);

This is inefficient because the MyClass object must be copied onto the stack. It would be more efficient to only pass the address of the object (pass by pointer), like this:

Code:
int funcByPointer(MyClass *x)
{
   //stuff
}

MyClass x;
funcByPointer(&x);

However some people find it confusing to have to deal with memory addresses directly, because an address might point to nothing at all..which would cause compilation errors. References are a way of telling the computer to use pointers without making the programmer have to think so much about safety. Using references, it can be done like this:

Code:
int funcByReference(MyClass &x)
{
  //stuff

MyClass x;
funcByReference(x);

Notice that using references, the code can be written almost exacty the same way as with pass by copy, except that the compiler uses pointers under the hood for increased efficiency.
 
Last edited:
In C++, a reference parameter in a function, as in "void squareCBR(int& x), has the semantics of an alias (alternate name) for the argument in the function call, e.g. "squareCBR(a)". As junglebeast notes, the compiler usually implements a reference by using a "hidden" pointer.

A significant practical difference between a reference and a pointer is that whereas you can change what a pointer points to, e.g.

Code:
int a = 5;
int b = 10;
int *p = &a;
cout << "p points to " << *p << endl;  // should display "p points to 5"
p = &b;
cout << "p points to " << *p << endl;  // should display "p points to 10"

you cannot change what a reference points to, in a similar fashion.
 
Dear junglebeast and jtbell,

thanks for your help! It has become very clear to me now.
I'd still like to mention why I didn't understand call by reference:

1) I didn't know what a reference is, e.g.
Code:
int x = 12;
int& otherNameForX = x;
otherNameForX = 24;     // The value of x is also changed to 24
As jtbell mentions, otherNameForX is just an alias for x.

2) The different uses of & :
Code:
int x = 12;
int* pointerToX = &x    // <--- &x is an address;
int& otherNameForX =x;  // <---- int& stands for a reference
I've thought about & only in the context of addresses.

3) I realized what a function actually does. For example:
Code:
void funcPointer(int* p){
      *p = *p + 2;
} 

int main(){

    int x = 12;
    funcPointer(&x);

return 0;
}

is equivalent to

Code:
int main(){

    int x=12;
    
    int* p = &x;
    *p = *p + 2;

return 0;
}

In short: What I realized, thanks also to junglebeast, is the following:
When passing an address &x to funcPointer(int* p) the following assignment happens:
funcPointer(int* p = &x).

The same goes for funcRef(int& alias). When passing x to the function the following assignment happens: funcRef(int& alias = x).

Realizing that this assignment occurs I can now understand what kind of "thing" I have to pass to the functions (either &x or x).

Correct me if this is wrong.
 
Last edited:
Yes references are just a way of hiding the pointer.
Also references are disliked by a lot of programmers because they don't look like pointers.

So "function(int*x)" is clear that it is passing an int that is likely to be changed, while "function(int& x)" looks like it is just passing an int but could be changing it.
Some common (although not universal) advice is to only pass const references.

So "function(foo *_foo)" would be called with function(&_foo) making it clear that you are changing _foo while "function(foo& _foo)" called with function(_foo) doesn't change anything.
 
In my experience, programmers who first learned pointers (in C or some other language) tend to dislike references, whereas programmers who first learned references (e.g. in Fortran or Pascal) tend to dislike pointers. :smile:

I first learned to program in Fortran many years ago, when arguments to Fortran subprograms and functions were always passed by reference, and pointers did not exist in the language. Therefore C++ references feel "natural" to me.
 
jtbell said:
When arguments to Fortran subprograms and functions were always passed by reference.
Stuff like this was always fun to do in early versions of Fortran:

Code:
      call settotwo(1.0)
      a = 1.0
c     a ends up == 2.0
      end

      subroutine settotwo(x)
      x = 2.0
      return
      end

some compilers treated (0) as a pointer, initializing x(0) to point to &x(1)

Code:
      integer pointer(1)
      integer array(10)

c     set pointer to array
c     after this, pointer(1) == array(1), access to pointer(1) is forever lost
      pointer[0] = array[0]

c     set pointer to absolute value (was used to access os system globals)
      pointer[0] = 32
 
Last edited:
Thank mgb_phys. You explained very well the "danger" of using references.
 
By the way, the "references versus pointers" argument is an old and common one among C++ programmers. Try a Google search on something like "references versus pointers in C++" and you'll get many points of view. :smile:
 
  • #10
mgb_phys said:
Yes references are just a way of hiding the pointer.
Not quite. For one thing, you can't change a reference. A reference someType & foo is similar to someType * const foo. Note that I said similar to, not equivalent to. There is no such thing as a null reference.

Some common (although not universal) advice is to only pass const references.
e.g., google: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Reference_Arguments#Reference_Arguments

I disagree with this rule. As mentioned above, there is no such thing as a null reference. This alone is, IMHO, a reason to prefer references over pointers. A defensive programmer will check that a pointer passed as an argument is not null prior to referencing the pointer. This opens several cans of worms. What to do if it is null? Do you throw an exception, abort, return an error code? Whatever the case, the use of a pointer as opposed to a reference has made the job of documentation a bit harder, possibly considerably so, and has added the need for a new test case to achieve 100% code coverage.

My projects do offer an out to the above: In the function's documented assumptions and limitations, specify that pointers are assumed to be valid (non-null). This is not a preferred usage of the assumptions and limitations; it usually becomes software ticket. Assumptions are intended to address physical assumptions like bodies are rigid, slow mass depletion doesn't affect dynamics, etc.

Our rule is to disallow pass by reference for primitive types but to prefer pass by reference for structured types.
 
Last edited by a moderator:
  • #11
D H said:
I disagree with this rule. As mentioned above, there is no such thing as a null reference. This alone is, IMHO, a reason to prefer references over pointers. A defensive programmer will check that a pointer passed as an argument is not null prior to referencing the pointer.
That can also be a problem.
If you want the pointer to be null to say that the function allocates the memory or you want a default value for an unused parameter you need to have some field inside the referenced object marking if it is valid or not which breaks RAII
 
  • #12
I agree. References cannot be used if the thing being pointed to legitimately can be nothing, is not known at the time the variable is instantiated, if the pointer itself can change, and I am sure there are other places where references just won't work. You have to use pointers in such cases, and that means you have to be a bit more careful.
 
Back
Top