Why does C not offer syntactically transparent references like C++ and Java do?

Viewed 250

I tried to learn more about the historical reason[s] for passing by value in C. There is a Stack Overflow post that touches on this topic in relation to C++. However, I'm interested in C. I imagine back when this decision was made, one could have opted to do what we call now "pass by reference". Yet, this was not the choice made. Are there optimization, multi-threading, etc., issues that come into play?

I tried to Google this, but didn't get anywhere. Passing by value presents some guarantees in relation to the variable not being changed under the hood. Is this the only reason? I imagine back then the concept of "copying a fat object" wasn't much of an issue and passing by value incurred small overhead. As such, this might have been deemed the right choice. Is there more to this decision than this?

5 Answers

In the words of Kernighan and Ritchie in The C Programming Language, 1978, clause 1.8 “Arguments — Call by Value,” page 24:

Call by value is an asset, however, not a liability. It usually leads to more compact programs with fewer extraneous variables, because arguments can be treated as conveniently initialized local variables in the called routine. For example,…

… When necessary, it is possible to arrange for a function to modify a variable in a calling routine. The caller must provide the address of the variable to be set (technically a pointer to the variable),…

  1. Passing by value is often faster than passing by reference and dereferencing a pointer every time you want to use an argument. As structures were not a part of early C, and then later required to be passed as a pointer, this was not a big problem.

  2. Many old compilers didn't or couldn't optimize dereferences, and dereferencing a pointer every time you wanted to use an argument would be very slow.

  3. Old computers needed micro-optimized code to run faster because the processors themselves weren't very fast.

  4. It gives the programmer the freedom to choose whether they want to use a pointer or a value to access data.

All that together probably amounts to why C uses pass-by-value.

C has always aimed to be well-performing and "close to the metal", and passing primitives (integers/pointers/floats) by references is simply suboptimal (primeval C didn't have structs, and when it did gain them, they were initially rather limited and you couldn't pass them by value or return them).

Passing by reference necessarily involves passing by an (implicitly dereferenced) pointer and if you're going to pass a pointer, you might as well pass the value directly and save the callee from having to fetch the pointer target.

Passing values directly also helps performance because you can stick to registers and skip the much slower memory.

Last but not least, it makes it easier to reason about code, because you know a callee may not change a variable you've passed to it by value.

In my opinion, the main reason to implement a so reduced complexity language was the environments supported on the ancient times of the C language design. The best computer affordable in 1976 to run unix (v5, v6, v7?) was a pdp-11, which is a machine with 64Kb (on the lasts you could separate instructions and data in two different segments to double virtual address space) of virtual addresses.

Needing to run a C compiler with only 64kb of virtual space makes pcc (portable C compiler, the compiler from at&t unix for the pdp-11/unix) to overlay the text segment to allow space for all the code to fit.

And finally, think that those programmers had to write all that code with ed(1) on a printing teletype !!!! (no flat, 40' screens with a full screen editor with 100x180 caracters, colored syntax or similar effects)

C was always intended to (a) be close to the machine and (b) give you basic mechanisms that you could do more complicated things with, if you wanted to. The choice of passing by value is the right one, I think, under both criteria.

C gives you pass by value, but if you want the effect of pass by reference, you can easily do with with &. (On the other hand, if the language gave you pass by reference and you wanted to simulate pass by value, you'd be kinda stuck.)

Using pass by value also sidesteps all the conundrums with pass by reference, like the notorious (alleged) behavior in Fortran where if you called f(5) and then f modified its argument, you ended up changing all the other 5's in the program.

Related