- "reference" definition 1: an alias that cannot be null which is what a "reference type" is in Pascal and C++. This might be thought of as a stricter "computer-science" definition. The C and Go languages don't have this type of "reference".
- "reference" definition 2: a pointer variable that lets programmers change the "thing pointed to" instead of the pointer variable itself to avoid copying unnecessary bytes. This is the colloquial usage. It also doesn't help that we call the star operator the "de-REFERENCING" operator[2] instead of the "de-POINTERIZING" operator. (What are we "derefencing" if it's not a "reference"?!?)
Cheney is talking about definition 1.
[1] http://www.di-srv.unisa.it/~vitsca/LAB/C-faq.html excerpt:
4.11: Does C even have "pass by reference"?
A: Not really. Strictly speaking, C always uses pass by value.
You can simulate pass by reference yourself, by defining
functions which accept pointers and then using the & operator
when calling, and the compiler will essentially simulate it for
you when you pass an array to a function (by passing a pointer
instead, see question 6.4 et al.). However, C has nothing truly
equivalent to formal pass by reference or C++ reference
parameters. (On the other hand, function-like preprocessor
macros can provide a form of "pass by name".)
[2] https://en.wikipedia.org/wiki/Dereference_operatorBefore languages had reference types or pointers, the distinction between pass-by-reference and pass-by-value (or also pass-by-name in Lisp and Algol) told you important things about the semantics of the language, such as whether a function you called could modify the objects you gave as arguments, or incorporate them by reference into some other object, and how arguments that are expressions are handled. Some of those distinctions become blurred when pointers or references are passed by value; the distinction on how you pass them now becomes a distinction on what you pass. In practice, is there any language that passes pointers or references by reference?
In languages with macros, you get to cheat a little, and write something that looks like the above without _really_ having those semantics. However, in languages like Lisps where your macro language is extremely powerful, you might not really miss it so much.
There is a lot of convenience in _not_ having pass-by-ref! Sometimes I feel like the ardent opposition just doesn't want to admit their favorite language is missing a feature ;-) If I call a function f(a, b), I know that a, b are still pointing at the same object in e.g. Python. That is not the case in C++ (pass-by-ref) or Lisp (arbitrary macros). Predictable language semantics make code easier to reason about. That's a good thing, and it's quite plausible that the added complexity and confusion of supporting pass-by-ref isn't worth the expressiveness.
I appreciate that the term is confusing, but it does mean something. I submit that making it mean two things would be even more confusing. If it's confusing, just say "passing (an argument) _as a reference_" which is unambiguously about what you're passing, and not how you're passing it.
In general, you can use pointers in Object Pascal, but the language offers a lot of options for avoiding them, if you want to, and you get type-checked references in the process. Records (structs) are stack-based and pass-by-value, by default, and class references/class instances (instances are always heap-allocated) are pass-by-reference, by default. So, for the majority of applications, you can use parameters in a myriad number of ways in order to improve performance or provide the proper calling semantics, without any of the hassle of address-of or de-referencing operators.
In Java, Python, Javascript and many other languages, objects pointers are passed by value, but the contents of which are shared between the caller and the callee, so the modification of which is visible from both sides. If we take the definition of pass-by-reference or call-by-reference semantics to mean that, then these languages are pass by reference by default. Apparently, that's not the definition, which in my opinion is rather strange that we are still stuck in a definition that predates OOP, when people mostly concerned with simple primitive values as opposed to composites.
It appears that Barbara Liskov has recognised this and coined call-by-sharing in 1974. 43 years later, practitioners are still arguing in the C++ frame of mind and failed to disseminate the more modern and suitable term of call-by-sharing. This is rather disappointing to me.
PBV and PBR seems to be a tiny distinction and yet, we have blog posts and interview questions and online debates about whether a language falls into the first or second category.
For PR reasons, they didn't want to have "pointers" (arithmetic, etc. ew). So Java has "references".
But what happens when you call a method on a null reference? Not NullReferenceException, but NullPointerException.
Well, it's just a rename for PR reasons. They actually don't have pointers.
>But what happens when you call a method on a null reference? Not NullReferenceException, but NullPointerException.
That's because it's the underlying pointer access that causes the exception. Doesn't mean the reference is itself a pointer -- it just abstracts over one.
It's possible that this may create confusion to other people more familiar with, for example, the C++ concept of passing by reference which is passing an actual reference (v. "passing a pointer").
[0] https://www.tutorialspoint.com/cprogramming/c_function_call_...
If I pass a java object to a function, I'm passing a (probably) 8-byte pointer to some memory with a class tag, fields, whatever. It goes on the stack just like an 8 byte long.
In languages that support pointers more directly, I'm doing the same thing, maybe minus the class tag in the pointed-to memory. Address is in an 8 byte type, put it on the stack and access the pointed-to struct in your new frame.
Yet I've seen interview questions about whether you're "passing by reference" or "passing a reference by value" like there's some big meaningful difference and one answer is wrong.
I don't get it. Is this just one of those nerd arguments where we're debating semantics for the sake of it?
The roots come from C, where pretty much every book on the subject talked about the difference between passing a huge, expensive value to a function vs passing a cheap pointer "reference" to it.
And while the semantic purists will scream and holler, the fact remains that, colloquially, a pointer is a reference to something, not the something itself. And so this annoying debate rears its ugly head a few times a year and makes it into HN or Slashdot or whatever.
I appreciate that it's confusing to someone who isn't familiar with the term, but so's most of CS. We had a term and it referred to a thing, and having it refer to two different things is worse.
Practically speaking, the most important concept is that of cost, which the naive answer covers. The distinction only matters in a language that actually differentiates the two. Otherwise it's little more than trivia, and certainly nothing to get worked up over.
Yet some semi-official sources (like The Go Programming Language book) do refer to channels as "reference types".
TBH, I find cheney's post not fully useful without explaining what does happen behind the scenes in maps so that passing them by value doesn't incur large copies.
But then refer to the Go team's article on maps:
Map types are reference types, like pointers or slices, and so the value of m above is nil; it doesn't point to an initialized map. A nil map behaves like an empty map when reading, but attempts to write to a nil map will cause a runtime panic; don't do that. To initialize a map, use the built in make function:
m = make(map[string]int)
The make function allocates and initializes a hash map data structure and returns a map value that points to it. The specifics of that data structure are an implementation detail of the runtime and are not specified by the language itself. In this article we will focus on the use of maps, not their implementation.
Saying maps are not passed by reference is a bit deceiving. The map's pointer location is not passed by reference.Knowing the difference can have important ramifications; particularly in the case of a slice which has its header copied when passed to a function.
It's actually a good proxy to determining company culture. If he starts to patiently explain the difference, I'll stop him and talk about the practical implications of the concept, and we'll have a good laugh.
If he gets annoyed or stiffens and things become tense, I'll have a good proxy for what to expect should I make the mistake of working there.
func fn(m &map[int]int) {
m = make(map[int]int)
}
...
fn(m)
You have to write: func fn(m *map[int]int) {
*m = make(map[int]int)
}
...
fn(&m)
It's very C. The caller is forced to use different syntax to pass something as a pointer/reference. In C++ and Rust, you can't see from the call whether you're passing a pointer or a reference.E.x. this works
int square(const int & num) {
return num * num;
}
int main() {
square(5);
}
this does not fn takes_ref(a: &i32) -> i32 {
*a
}
fn main() {
takes_ref(64);
}
There are still cases where you won't be able to tell if you're using a ref or not, though: let x = returns_a_ref();
takes_ref(x);> It is not possible to create a Go program where two variables share the same storage location in memory.
It is possible to do so, because the empty struct occupies no space. For example:
s := []struct{}{
struct{}{},
struct{}{},
}
fmt.Printf("%p %p\n", &s[0], &s[1])
> 0x176f44 0x176f44
But that's kind of a fringe case, and I suppose one might argue that the two variables occupy no space so in a sense they don't share that space. Either way it doesn't detract from the original point of the post :) void f(map<int, int> m)
{
m[0] = 0;
}
int main() {
map<int, int> t;
t[0] = 1;
f(t);
cout << t[0] << endl;
return 0;
}
prints 1.Go:
func f(m map[int]int) {
m[0] = 0
}
func main(){
var t = make(map[int]int)
t[0] = 1
f(t)
fmt.Println(t[0])
}
Prints 0.So in C++ maps are passed by value. In Go they are passed by, hmm, "not value".
Go could have made maps similar to C++ maps and then people could use a pointer to a map when they wanted that. As it stands maps are "different" which is not a big deal once you're used to it but still makes things a little weird if you're coming from C++.
https://tour.golang.org/moretypes/1
Does anyone know of an imperative language doesn't have references, but also doesn't have pointers? Perhaps using something like copy-on-write to pass variables to functions.