C vs C++: pass by value, or by reference

A quick question to those who remember C better than I do.

C++ has the concept where when one passes parameters into a function/method, one can do it by value, or by reference. When passed by value, essentially copies are passed into the function, and changes made to those values are not seen in the calling code. When passed by reference, changes made in the called function to those values are seen in the calling code. Ampersands are used to distinguish between the two methods.

Does C have this concept? I cannot remember.

In C, you would pass a pointer to access the actual data.

And if you don’t pass a pointer, but the value, then any changes to those values in the called function are not seens in the calling code, right?

Correct. Hence the name, pass-by-value.

Strictly speaking C is only call by value (and by extension so is C++). However pointers are used to do exactly what you are describing (provide a means for call by reference). For example suppose you wanted a function that added one to an integer you defined in your calling function:

int main() /* Note that you’ll have to flesh out the proper args and return value */
{
int MyInt;

MyInt = 0;
AddOne(&MyInt);

printf("%d

",MyInt);
}

void AddOne(int *value)
{

*value = *value + 1;
}

So main should print out a 1. Now the argument to AddOne doesn’t specify an integer, but rather a pointer to an integer, that is, **value **holds an address that when dereferenced, holds an integer. So note that the * operator is what dereferences a pointer in C and C++. So referencing **value **without the * operator tells you what memory address the integer is at, and changing it (i.e. value = 10) affects it only for the scope of the function (that is call by value).

Also, the amperstand is really the complementary operator to *, so you probably shouldn’t think of those operators in terms of call by value/reference.

I’m sure someone else will be along to explain better, but if you want a short answer, then yes, C as well as C++ have a mechanism for call by value and call by reference. The longer answer is “yes, but”.

Take care,

GES

Thanks all. This came up on an interview a couple weeks ago; the woman interviewing me showed me some code and asked what the output would be.

The code was clearly passing parameters by value, so I described the output as if the changes in the called fucntions did not come back to main().

She said I was wrong!!! I knew I was correct if the code was C++, but I started to wonder if maybe the code was C, and I couldn’t remember how it worked in C.

I was still right dammit.

Revtim - are you sure the incoming parameters in the called function aren’t declared as references ? You may know that these are like “pointers with sugar on” in C++. When a function declares a paramter as a reference, that function gets to modify the parameter you pass in, and you, the caller, will see the change, even though as far as you’re concerned, you didn’t pass in the parameter by reference.

Reference on references: :wink:

Reminds me of an awful pun - maybe apocryphal, but I find it entertaining. The story goes that Niklaus Wirth, inventor of Pascal, was asked during a lecture, “How do you pronounce your name? Is it ‘worth’ or ‘vert’ ?” He answered, “It depends if you’re calling me by name or by value.”

Yes, the parameters had no ampersands.

The parameters you pass don’t have to have ampersands, though. It depends on the function definition. (I’m not sure if that’s clear.)


#include <iostream>

void AddOne(int &i)
{
++i;
return;
}

int main(void)
{
int five = 5;
AddOne(five);
std::cout << five; // output is 6

return 0;
}

When you pass by reference, the pointer work is done for you. So it is entirely possible to pass a variable itself rather than pass by value. Without knowing if the function takes a reference or a value, you can’t say what will happen. Most visual environments will display the function(s, if overloaded) as you call it so you’ll know if you’re passing the value or the variable itself.

Passing by reference saves memory overhead since a temporary variable doesn’t have to be instantiated to work on… very good when you pass a class or structure as an argument, less important when you pass a lone integer.

The alternative construction using pointers is


#include <iostream>

void AddOne(int *i)
{
++(*i); // will increment value without parenthesis, unlike *i++, which will do pointer arithmetic
return;
}

int main(void)
{
int five = 5;
AddOne(&five);
std::cout << five; // output is 6

return 0;
}


Which does the same thing.

In C++, function parameters that are anything other than a fundamental type (an integer, a floating point number, a character, or a pointer to one of those) should be declared as references. There are two reasons for this:

  1. A reference is really a pointer, as was previously mentioned. As such, it takes 4 bytes. An object has the size of all its data members put together, which is probably larger than 4 bytes. So you save space. This is nice in most systems and critical in embedded systems.

  2. The second reason refers to something called the object slicing problem. Suppose I have two classes A and B, where B is derived from A. They share a virtual function foo(). If you have a function bar(A a), and pass in an object of type B, it gets sliced to an A. All the virtual functions and data members associated with B are stripped off and lost forever. In some situations, this isn’t bad, but I don’t think it takes much imagination to see how it could be really bad.

The example used neither ampersands for reference, nor pointers.

What was the example, as best you remember? That’s just incredible that someone, who the company viewed as skilled enough to be interviewing, would make so basic a mistake.

And why did she not tell you what language? Why didn’t you know whether it was c or c++?

She was a manager, and clearly not skilled in code. The code she showed me was on a printout, and she was likely only coached on what someone thought the correct answer was, and not prepared to evaluate or understand the answer.

The code was simple main function, that:

  • defined two variables
  • set and output their values
  • called a swap function (by value!)
  • output the values
  • called a second swap function (also by value)
  • output the values.

Since the job was for someone with C++ experience, I assumed it was C++, but later realized it had nothing C++ specific (it used printf and not cout, for example) and could have been compiled with a C compiler. IIRC, she referred to it as “this code” or something less specific.

I opened this thread because I started to wonder if maybe I was wrong because it was C and not C++, but since it didn’t pass values by pointer, I think I was right regardless.

In my “thank you” letter for the interview, I politely explained in detail (in C++ terms, since it hadn’t occurred to me yet that it might have been C) why I was right, but it’s been 2 weeks and I think that ship has sailed.

Revtim - you still haven’t told us whether in the signature of the called function, the parameters were declared as references. Don’t confuse references with pointers - they are totally different (though references may be implemented as pointers under the covers).

The point (pun unavoidable) is that it doesn’t matter how you pass the parameters - if the function you’re calling declares them as ‘references’, then that function gets to modify your parameters, just as if you’d passed them ‘by reference’. That is why some functions declare their arguments like this:

void DoWork(const SomeBigObject& foo)
{ … }

It’s so that you don’t incur the overhead of passing the whole of foo (which would happen if you’d passed it by value), yet thanks to the ‘const’ you gain the “read-only” aspect offered by pass-by-value.

If he knew they were swap functions, it’s pretty clear he saw the function definitions. He said the arguments weren’t declared as pointers or references.

Yes, as I said before, there were no ampersands, nor asterisks.

Revtim, is it possible that the swap routine operated directly on the external variables that were to be swapped? That is, the parameters to the swap routine were decoys? Maybe they had similar names and they were testing to see how closely you looked at the names, or if you would notice a reference to an external variable where you weren’t expecting it.

I suppose it’s possible, but I think I would have noticed that. But it’s been a few weeks, so my memory of the program is getting less reliable.

This is true. I heard him say this when he visited Illinois when I was a grad student. Not those exact words, of course, and I got the impression that he used this joke for every talk.

Hey, does this mean I can be a cite?

For fun, you should look up call by name in Algol 68, done with a data structure called a thunk, not at all related to the Jefferson Airplane song of the same name.

Silly question, but is it possible that she was more interested in hearing how you argued your point?