A strong answer
Passing by value copies the argument into the function's own variable. The function works on the copy, so the caller's variable is safe, and the cost is the size of the copy: nothing for an int, a lot for a large struct or a std::vector in C++.
Passing by pointer copies an address, 8 bytes on a 64-bit machine, however large the object. The function reaches the caller's object through it and can change it. A pointer can be null, so the function must check, and it can be reassigned to point elsewhere.
A C++ reference is also passed as an address under the hood, but the language treats it as another name for the object: it must be bound to an object when it is created, can't be reseated, and can't legally be null. const T& is the usual way to pass a large object you only read.
Rules of thumb: pass small types (integers, pointers, small structs) by value, because following a pointer costs more than copying 8 bytes. Pass large objects you only read by const reference. Use a non-const reference or a pointer only when the function is meant to change the caller's object, and a pointer (or std::optional) when "no object" is a valid input.
Garbage-collected languages hide the choice: Java, Python, C# and JavaScript pass object references by value. The function gets a copy of the reference, so it can change the object, but assigning a new object to the parameter doesn't affect the caller.
What the interviewer is listening for
- What is copied in each case, and the cost of the copy.
- Null and reseating as the real differences between pointers and references.
- A clear rule for choosing, with performance and intent as the reasons.
Common mistakes
- Saying references are "faster than pointers". Both pass an address.
- Saying Java passes objects by reference. It passes references by value.
- Returning a reference or pointer to a local variable, which dangles once the function returns.