Like Stones: Argument Passing in C++, Part 1

For people new to C++, argument passing can be very hard to understand. Most programming languages handle argument passing “behind the scenes” somehow. Either there is really one form of argument passing (e.g. C), or your choices are determined for you (e.g. Java). Not so with C++. Like almost everything else, C++ requires that you handle argument passing yourself.

So, for those people, I thought I’d write up a couple of posts about the various types of argument passing. But be aware: despite what you may think, this is not really an article for beginners. I wrote this mostly for people who already have some programming experience in another language, but are new to C++. It doesn’t require a lot of knowledge, and I’m not saying that beginners won’t get anything out of it. But if you are an absolute beginner, you may be overloaded with information.

With that out of the way, let’s talk about C++. The short version is that C++ has three mechanisms for argument passing: passing by value, passing by reference, or passing by pointer (or “simulated reference”). I’ll explain how each one works in turn.

In this article, I’ll explain how each works with primitive variables and arrays. Passing objects is more complicated, so I’m going to leave that for a second article.

First, a note about terminology. When I say “function,” I mean it in a very general sense. Obviously, this applies to stand-alone functions, including the main() function. But it works exactly the same way with class functions, whether they be member functions (methods), constructors, or overloaded operators. Also, when I say “passing,” I’m not just talking about passing into functions as parameters, but also returning from functions. (Think of a return statement as “passing back” to the function’s caller.)

Functions and Stack Frames

To truly understand how passing works, let’s look at how functions are called at the machine level. This is a deep subject, and I’m only skimming the surface. If you need to read this article, you won’t need to understand the mechanics in detail; but you should at least get the general gist of it.

You are probably aware of machine architecture in general, but in case you’re not, I’ll cover it briefly. Generally speaking, there are two places to hold information: in random access memory, and in CPU registers. Registers are often in short supply, so most operations must use memory locations.

There are two general types of memory: static memory and dynamic memory. Static memory holds any data that is determined at compile time, like static variables (regardless of scope) or global variables. Dynamic memory holds any data that is created and destroyed during the program’s runtime. Dynamic memory, in turn, is divided into stack memory and heap memory. Heap memory (sometimes called the “free store”) is either managed by the programmer explicitly (C or C++), a managed runtime environment (Java or C#), or an interpreter (Ruby, PHP, or JavaScript). In C++, memory is allocated on the heap using the new keyword, and de-allocated using the delete keyword. C uses the malloc(), calloc(), and free() functions from stdlib.h.

Stack memory is the type of memory that we are concerned with here. It is so called because it is managed like a first-in-last-out stack, so the last thing allocated on the stack must be de-allocated before anything else on the stack is de-allocated. This does not mean that the memory must be accessed in a first-in-last-out fashion; it is still random access memory. This makes stack memory very easy to work with. If you know the order in which variables are created, you automatically know their memory locations, as offsets from the top of the stack.

Whenever a function is called, a stack frame is created. A stack frame is a series of values that are pushed onto stack memory, in a pre-determined order, every time there is a change in scope. For a function call, the stack frame includes:

  • The address of the caller
  • The current location of the top of the stack
  • The values of any registers that will be overwritten (“clobbered”) as the function executes
  • The actual arguments (if any are passed)
  • The (non-static) local variables

Who saves and restores these values (caller/callee), and in which order, is dependent upon the machine architecture and the operating system. And stack frames may contain additional information (for example, a method’s stack frame will contain the location of its associated object). But all stack frames store this information in some form or another.

As the function returns, the stack is “unwound.” This is generally what happens:

  • Registers that the function used are restored to their previous values
  • The return value is stored (either on the stack or in a specific register, depending upon OS/architecture)
  • The value for the top of the stack is restored to its previous value
  • The program returns to the address of the caller, and execution continues

Any further use of the stack means that the old stack frame is overwritten. As you can imagine, this will happen pretty much immediately. The effect is that all of the data held by a function (including parameter values and local variables) is effectively wiped out after the function returns. A function is only in scope when it has a stack frame, which is why non-static local variables only have the scope of their defining functions. The local variables’ scope is automatically enforced by the creation and destruction of the stack frame. This is why they are called automatic variables.

(In contrast, static local variables are stored in static memory, along with global variables. Their scope is enforced by the compiler, not the machine code. You don’t need to know how the compiler does this; just know that it does.)

Incidentally, this is why recursion is dangerous if not handled properly. With every single recursive call, a new stack frame must be created. If you do too many recursive calls without returning, the stack frames will eat up all of the computer’s memory. This is where the term “stack overflow” comes from.

Now that we have a general idea of how function calls work, let’s continue our talk about argument passing.

Passing by Value

This is the default mechanic for argument passing in C++. This is not surprising, because behind the scenes, it is the only mechanic for passing arguments in C, upon which C++ is based.

It works like this. Let’s say you have an integer variable x, initialized to 3. You pass it to a function f which has one integer parameter, identified by y, and it returns nothing (void). In its definition, the function simply increments y by one, using the unary post-increment operator.

Let’s see it in C++ code:

// in main()
int x = 3;
f(x);

// f's definition
void f(int y)
{
  y++;
}

Here’s the million-dollar question: What happens to the value of x? Is it 3 or 4?

The answer, as you hopefully could guess, is that it is still 3. This is because it is y that is being incremented, and the scope of y is limited to the scope of f.

So, how does y get the value of 3? The answer is that the value of x is copied into y. This happens as the function is called, and before the body of the function is executed. This is why it is called passing by value.

Passing by value has a lot of benefits, so it’s no surprise that it’s how arguments are passed by default. It’s easy for programmers to understand. It obeys the second rule of pure functions, which says that functions may not alter the arguments that are passed to it. And it is relatively easy for the compiler to implement.

But there are cases where passing by value is a bad idea:

  1. You are passing an argument representing a lot of data.
  2. Your function is designed to modify the argument that is passed to it. (In other words, it is not supposed to be a pure function.)

In the first case, passing by value would work, but it would be incredibly inefficient: you would have to copy all of the data that is passed to it. But the second case would be totally impossible if we could only pass by value. What’s a girl to do?

In C (the precursor to C++), the answer was to use a pointer. Since they’re common to both C and C++, I’ll talk about them next.

Passing by Pointer

A pointer should be considered like any other type of variable, except that it holds a very specific type of value. The value that it holds is a memory address. Pointers also keep track of the type of data that is located at a memory address; so, for example, you could say a pointer is a “char pointer” or a “pointer to char.” (You can also have a pointer to void, signifying that the pointer’s type is unspecified, but it’s almost never used in C++.)

When you work with the data at a pointer’s memory location, you are dereferencing a pointer. (It is perhaps an unfortunate term, since pointers and references are not the same in C++). This is why pointers keep track of the types of data that they point to; otherwise, it would be nearly impossible to dereference them, as the program would not know how to use the data it’s dereferencing. (Note that the type is enforced by the compiler; at the machine level, a memory address is just another number.)

The syntax for creating a pointer is to place an asterisk between the type and the variable name:

int* ptrToInt;

To dereference a pointer, you place an asterisk in front of the variable:

*ptrToInt = 5;

If you think this would cause some confusion, you’re not alone. Consider this code, which creates and initializes a pointer:

int *ptrToInt = 5;

You might think that you are creating a pointer to an int, dereferencing it, and storing 5 at the dereferenced memory location. But you would be wrong. What you are really doing is creating a pointer to an int that supposedly resides at memory location 5. Naturally, the memory location 5 is unlikely to hold any kind of data that should be interpreted as an integer; in fact, it’s probably some place in the program’s instruction code. This is a good way to cause memory corruption (assuming the location isn’t read-only), and modern compilers simply won’t allow it.

To avoid this confusion, I like to place the asterisk next to the type when declaring a pointer, and next to the pointer’s identifier when dereferencing it. This reinforces the viewpoint that pointers should be considered distinct types.

Pointers should be assigned (or initialized to) the address of an already-existing variable’s memory location. You get access to the variable’s memory location using the (again, unfortunately-named) reference operator, usually called the “address-of” operator. The syntax for the address-of operator is to place an ampersand (“&”) before the variable’s identifier.

So, here is the correct way to initialize a pointer:

int someInt = 5;
int *ptrToInt = &someInt;

As with setting a pointer to 5, above, modern compilers will complain if you attempt to assign a value to a pointer that isn’t an address.

Note, however, that pointers don’t need to be initialized. Just like non-pointer variables, they can be declared before they are ever used. When you attempt to use a non-pointer variable before it’s been initialized, you get garbage. Pointers, on the other hand, are always initialized to zero. A pointer that points to zero is called a null pointer.

This leads to a long-running headache of using pointers: there’s no way to guarantee that they’re actually pointing to anything. If you attempt to dereference a null pointer, your program will crash.

A pointer to zero corresponds to the nullptr keyword, which was standardized in C++11/C++0x, but supported by most compilers much earlier. The NULL constant (borrowed from C) is also used quite often, but is discouraged. It is recommended to use nullptr because it won’t be implicitly converted to the int value of zero. (Think function overloading.)

So, assuming a pointer is pointing to a valid address, is there any limit to where it can point? No, there isn’t. This means that you can pass a pointer to a function as an argument, and when you dereference that pointer, you affect whatever is at that memory location, even if it’s outside the function’s scope.

Let’s rewrite the f function to accept a pointer. Now, when we call it, we will need to pass the address of x, rather than x itself, using the address-of operator. Here’s the new code:

// in main()
int x = 3;
f(&x); // Pass the address of x

// f's definition
void f(int* y) // y points to &x
{
  *y = *y + 1;
}

Here’s the same million-dollar question: What happens to the value of x? Unlike passing by value, we are incrementing the value at the address y points to by one. Since y points to x, x‘s value is incremented, and x is now 4.

We now have a function that is not a pure function, and can alter the variable that is passed to it. This also solves the problem of passing large objects to functions. A pointer takes up only enough memory to hold an address (usually the same memory as an int). By passing a pointer to a large object, the function is able to get at all of that object’s data by dereferencing the pointer.

Here’s something else. Remember that I said that passing by value is the only mechanic for passing arguments in C. But C has pointers, so what did I mean?

What I meant is that the value of a pointer variable is copied to a function’s parameter, just as if it were a non-pointer variable. It’s just that its value is a memory address rather than (say) a char. But otherwise, it is exactly like a non-pointer variable; it has its own memory location, it takes up memory, and its value is still copied before being passed.

To see why this matters, consider the following C++ code.

// in main()
int x = 3;
f(&x); // Pass the address of x

// f's definition
void f(int* y) // y's value is copied from x
{
  int z = 6;
  y = &z;
  *y = *y + 1;
}

The address of x is passed by value to the pointer-to-int y, but within the function, y is dereferenced and assigned the address of local variable z. It is z‘s value that is incremented. Since the address of x was copied, the original value of x was not changed.

That’s a good thing; otherwise, x would be pointing to an invalid address on the stack. After f returns, z is no longer in scope; the stack has unwound, and the next time anything is allocated on the stack, z’s value is overwritten. This is trivial in an example like this, but it will have big consequences when we talk about passing objects.

This sometimes results in hard-to-find bugs. Let’s rewrite the code from above, but use the unary increment operator in the function:

// in main()
int x = 3;
f(&x);

// f's definition
void f(int* y)
{
  *y++; // what happens here?...
}

You would think that the value of x would be 4, but you would be wrong. After the function is called, x is still 3. This is because the dereference operator actually has a lower precedence than the unary increment operator. What you are actually doing is incrementing the address y points to by “one.”

I put “one” in quotes becuase it’s not actually a value of one. Instead, it does pointer arithmetic. Whenever you add “one” to a pointer, you are increasing the pointer by the amount of memory that the data type requires. In other words, the value returned by sizeof().

Let’s use the above function as an example. In most systems, an int type requires four bytes. For the sake of argument, let’s say that the variable x is located at memory location 0xffff1c. When the function is called, that value is copied into the variable y. Within the function, the unary operator is called – and the value of y (the memory location) gets incremented by 4 bytes.

The location pointed to by y is now 0xffff20 – the original value plus four. What would happen if you tried to dereference y? Nothing was ever allocated at that location, so you would get some garbage value. Fortunately for you, the memory location was copied by value into y – so x itself won’t be affected.

To get around this, you can use parentheses, which have a higher precedence than anything else:

(*y)++;

It sounds like a PITA, and it is, so why does pointer arithmetic even exist? The answer has to do with arrays – which are passed by “simulated reference.” But to know what a simulated reference is, you first have to know what a “reference” is. So I’ll talk about that next.

Passing by Reference

In C++, a reference is an alias of another variable. You can think of a reference as an “alternate name” for the variable it references. A reference does not have its own address, and (in theory) does not take up any space in memory. It must always reference some other variable (there is no “null reference”), and cannot be “re-assigned” to reference a different variable.

The syntax for creating a reference is to place an ampersand between the type and the variable name:

int& refToInt = x;

The ampersand is the same symbol used for the “address-of” operator, but they should not be confused. This is similar to the asterisk being used for declaring a pointer, and for pointer dereferencing. For the same reason, I always place the ampersand next to the type, to make it clear I’m declaring a reference, and not taking the address of another variable.

Because references are aliases, you must always initialize a reference to another variable when you declare it. Unlike a pointer, you cannot declare a reference, then assign a variable to it later.

And, because it must alias another variable, a reference cannot be initialized to a literal or the result of an expression. In programming terms, a variable is an lvalue: a “location value,” or a value that can be on the “left” in an assignment statement. An rvalue is anything that can be on the “right” in an assignment statement, including literals and the results of expressions. For this article, “rvalue” simply means “anything that is not an lvalue.” For a decent explanation from (shudder) Microsoft, see Lvalues and Rvalues on MSDN.

What all this means is that references must always be initialized to an lvalue. In other words, you can’t do this:

int& r1;         // uninitialized reference (no lvalue)
int& r2 = 3;     // rvalue (literal)
int& r2 = x + y; // rvalue (result of expression)

There is one exception to the “no literals” rule. You may initialize a reference with a literal, but only if the reference is declared constant. In the following code, r1 will work, but r2 won’t:

const int& r1 = 5; // OK
int& r2 = 5;       // Won't compile

So, how do you “dereference” a reference variable? You don’t – or, more precisely, you don’t have to. References are just aliases, so no special syntax is needed to operate on the referenced variable; you are always operating on the referenced variable. This makes dealing with references much easier, syntactically speaking.

In the case of function parameters, this means that calling a function that accepts a reference is exactly the same as calling a function that accepts a non-reference. In both cases, you just pass in a variable as an argument. As a consequence, references can’t overload non-references in function signatures. The following code won’t compile:

// Function signatures
void f(int);  // Accepts an integer
void f(int&); // Won't compile; see below...

// in main()
int x = 5;
f(x);         // No way to know which f() we're calling!

Of course, you can avoid this simply by renaming f to something else, so that you’re not trying to overload the function. In practice, you wouldn’t want to overload a function with reference and non-reference parameters; even if it were allowed, it would just be confusing.

All of this makes references easier to work with than pointers. Consider the code that gave us problems earlier. Here’s the version that uses references instead:

// in main()
int x = 3;
f(x);

// f's definition
void f(int& y)
{
  y++; // what happens here?...
}

The parameter y automatically references x, without us having to do anything. This means that after you call the function, the value of x is in fact 4 – like we expected when we wrote the pointer version. We don’t have to worry about dereferencing, and there is no such thing as “reference arithmetic.”

Of course, if y were a const int& instead, then this wouldn’t compile. You can’t change the value of a constant reference, any more than you can change the value of a literal. So, y++ would not compile for exactly the same reason that 3++ won’t compile.

But here’s a neat trick. If a parameter is declared as a constant reference, you can pass any variable to it of the same type, even if that variable is not constant. For example, the following code compiles just fine:

// in main()
int x = 3;
f(x);

// f's definition
void f(const int& y)
{
  cout << "y's value is " << y << endl;
}

This makes passing by constant reference very useful. If you have a function that accepts a large data type (like an object with a lot of memory), you don’t want to copy all of that data when you pass it to the function; but you also want to make sure that the function does not alter the parameter in any way. Functions like these should take a constant reference as an argument. It is very, very common to do this when a function does nothing but output a parameter’s value(s), as above. (You’ll do this if you overload the stream output operator for a class that you write.)

Because this is common practice, it is generally assumed that if you pass by a non-constant reference, you are going to be modifying the variable passed to the function. If you’re not going to modify the variable, have your function accept a constant reference, to make this clear to everyone using your function (including yourself).

References seem easy (and they are), but there are things you still need to consider. Remember how a reference must be initialized to a variable when it is created? This is done so that a reference can never be null. The compiler tries its best to enforce this, but there are certain times when it can’t, and you have to be careful to watch out for these cases. Consider the following code:

// in main()
int& x = f();

// f's definition
int& f(void)
{
  int y = 5;
  int& ry = y;
  return ry;
}

When it is run, what is the value of x?

You may think it is 5, but you would (probably) be wrong. Remember our discussions about stack frames. The variable y is a local automatic variable, which means that when f terminates, the stack is unwound, and y is destroyed. Outside of the function, our variable x now references something that is no longer in memory.

This is what is called a dangling reference. In the world of C++, creating a dangling reference leads to “undefined behavior.” Your program could run for a while, then crash; or it could crash immediately; you could just be working with garbage values; or the stack may just never unwind, possibly leading to stack overflows. It all depends upon the compiler, the OS, and a host of other things. But whatever happens, it will be bad.

To avoid dangling references, you should never, ever return a local variable by reference. In fact, you usually shouldn’t return by reference at all. (The exceptions have to do with chaining operations, which I will cover in the next article.)

Before I finish the section on references, I must mention the fact that C++11 introduced additional types of references, including “rvalue references.” These are non-constant references to temporary objects. Using them is an advanced subject, so I won’t cover it here. If you’re truly curious, see Move semantics and rvalue references in C++11 by Alex Allain. If you want to go even further down the rabbit hole, read A Taxonomy of Expression Value Categories (PDF) by William M. Miller.

Up until now, we’ve only passed primitive types by reference. This can be useful, but it’s not the main reason programmers use references. As I suggested earlier, the biggest benefit of references is that you can pass large data types to a function, without having to copy all of that data. This is also one of the benefits of passing by pointer.

Disregarding objects for the moment (I’ll cover them in the next article), the biggest data type in C or C++ is the array.

Arrays: Passing by Simulated Reference

You probably know about arrays already, but perhaps not about how they are implemented in C and C++, so I’ll cover it briefly.

Arrays are compound data types that hold a number of values of the same type. In C and C++, the size of the array must be declared when initialized. Each value stored in the array is called an element of that array. To access an element, you specify its index within a set of square brackets. Arrays are zero-indexed, meaning that the first element of the array has index zero.

One quick note for people coming to C++ from other languages. Some other programming languages (I’m thinking mainly of PHP) consider arrays to be “associative arrays.” This means that the array index can be a string. In reality, these are not “arrays” at all, but dictionaries – hashes whose keys are strings. In some of these languages, you can access an array’s element using either its key, or its index. But C++ (like both C and Java) does not do this; array indexes must be a positive integer. (If you want similar functionality in C++, you must use a std::map from the standard template library.)

Also, like C (but unlike Java), C++ does not do bounds checking. If you have a 100-element array named (say) arr, then C++ will happily parse arr[105] without complaint. You will, of course, get some garbage value, but that’s of no concern to the C++ compiler. This can often cause problems, and it’s why array sizes are usually defined by some constant somewhere in the program.

In C and C++, all of the elements in the array are stored continuously in memory; there are no gaps between the elements. (This makes them unlike arrays in other languages, like Java or PHP.) This means that array sizes must be known at compile time; you have to declare an array’s size when declaring the array, and furthermore, the size must be a constant value.

Arrays that are declared are not initialized; unless you initialize them, they contain garbage. As a shortcut, you can initialize an array by specifying its values in a set of curly braces.

Here is code that initializes an array of five doubles, then prints out the fourth value:

double balance[] = {75.0, 20.0, 3.5, 19.99, 50.0};
cout << balance[3]; // Prints out 19.99; remember zero-indexing

If you have an array with 100 elements, you certainly save a lot of memory if you pass either a reference or a pointer. So, how do arrays work? Are they passed by reference, or are they passed by pointer? It turns out that the answer is neither. At least, not exactly.

Remember that C does not have references at all, and that C++ inherits everything from C. But this is perfectly valid C code, thus perfectly valid C++ code:

// in main()
double balance[] = {75.0, 20.0, 3.5, 19.99, 50.0};
printDoubles(balance, 5);

// function definition
void printDoubles(double[] arr, int size)
{
  for (int i = 0; i < size; i++)
    cout << arr[i];
}

The arr parameter is not declared to be a pointer type, nor is it declared to be a reference. This makes it seem like the array is being passed by value. But it is not. In fact, neither C nor C++ even allow you to pass an array by value; it is simply not an option.

Well, if it’s not by value, then the next best guess (from the syntax) would be that it is passed by reference, since the parameter variable doesn’t use the dereference operator. But this would also be a bad guess.

Instead, what happens is that arrays are passed by simulated reference. Behind the scenes, an array variable is converted to its base address: the address of its first element. Likewise, any function that declares that it is accepting an array is “really” accepting a pointer. This is why it is called “simulated reference.” What you are actually working with is a pointer; the language merely “simulates” the syntax of passing by reference.

So, the following function signatures are equivalent:

void printDoubles(double[], int);
void printDoubles(double*, int);

Likewise, the following function calls are also equivalent:

printDoubles(balance, size);
printDoubles(&balance[0], size);

The fact that arrays and pointers are equivalent is where pointer arithmetic comes in. Remember that when you add “one” to a pointer, you’re adding the number of bytes that each element requires. What this means is that pointer arithmetic and array indexing are also exactly equivalent. When you use the index operator (the square brackets), you are using pointer arithmetic to add that index to the base address, then dereferencing the result.

So, the following indexing operations are, once again, equivalent:

balance[3];
*(balance + 3);

But it is called “simulated reference” for a reason. Despite what some textbooks (and, unfortunately, a few professors) may tell you, arrays are not passed by reference. At least, not by a C++ reference.

The following code will not compile:

void f(int& arr)
{
    cout << "Element 2 is " << arr[2] << endl;
}

Likewise, passing an array as a reference will also fail to compile:

// function signature
void f(int&);

// in main()
int arr[5] = {1, 2, 3, 4, 5};
f(arr); // Nope.

You can switch between arrays and pointers at will, but you can’t switch between arrays and references.

If you’re curious – like I am – then you might wonder if there is a way to pass an array by bona fide reference. It turns out there is. Passing a reference to an array is possible using this syntax:

// function signature
void f(int (&arr)[100]);

The parentheses are necessary, because the compiler interprets int &arr[100] to be an array of references. (If you’re confused, read it as int& arr[100].) This is illegal in C++, because references (theoretically) take up no memory space whatsoever, so there is no way to allocate space for the array. Also, unlike passing by simulated reference, you must specify the number of array elements.

I have no Earthly idea why anyone would even consider doing this. I know nobody who has even tried. I mention it here merely for the sake of completeness.

References in other languages

Naturally, C++ is not the only language that has something it calls a “reference.” But it is important to note that C++ references are not the same as references in other programming languages. Most languages mean “simulated reference” when they say “reference.” Behind the scenes, they are using pointers, and passing the memory address by value.

For example, Java programmers often think that objects are passed by reference, but in reality, they are passed by simulated reference. See this article by Scott Stanchfield for details.

Python collapses the notion of “pass by value” and “pass by reference” into the term “pass by assignment.” In reality, this is the same as passing by simulated reference, while considering everything to be a reference type. See Chapter 18.1 of Learning Python by Mark Lutz – just keep in mind that he says “pass by value” when a C/C++ programmer would say “pass by constant, simulated reference.” Similarly, Ruby’s “reference types” are also passed by simulated reference, and Ruby also considers all types to be reference types, as this article by Khaled alHabache explains. (When I find an article with better grammar, I’ll let you know.)

On the other hand, PHP references are aliases in the symbol table. See the documentation on php.net for proof. This is closer to how C++ references behave.

So, what does C++ do “behind the scenes?” How does a C++ compiler implement references – as syntactic sugar for a dereferenced constant pointer (like most languages), or as an alias in a symbol table (like PHP)?

The best answer, I think, is this one: it doesn’t really matter. To quote the person who actually created C++:

The obvious implementation of a reference is as a (constant) pointer that is dereferenced each time it is used. It doesn’t do much harm to think about references that way, as long as one remembers that a reference isn’t an object that can be manipulated the way a pointer is[…] In some cases, the compiler can optimize away a reference so that there is no object representing that reference at run time.

  • Bjarne Stroustrup, The C++ Programming Language, 4th Edition

The next article on this subject will cover passing objects in C++. You’re waiting with baited breath, I’m sure.

About Karl

I live in the Boston area, and am currently studying for a BS in Computer Science at UMass Boston. I graduated with honors with an AS in Computer Science (Transfer Option) from BHCC, acquiring a certificate in OOP along the way. I also perform experimental electronic music as Karlheinz.
This entry was posted in c++, Programming and tagged , . Bookmark the permalink.

1 Response to Like Stones: Argument Passing in C++, Part 1

  1. Ron Newcomb says:

    Regarding the final section, “References in other languages”, does it not occur to you that, beside PHP, Java, and Ruby, you need to add C++ itself in there? “C++ has a feature called aliasing which it sometimes calls references…”

    Before OOP came around in the early 90s to muddy the definition, “reference” was a perfect synonym for “pointer”. There are only two choices, pass-by-value and pass-by-reference. At the hardware level, there is no third option.

Leave a comment