skip to content

Under call by value versus call by reference, what exactly does a call copy when a routine is handed a sensor record to fill?

level: juniorimportance: must knowfreq 78%

answer

  1. ask what the call copied
  2. a value, or a location
  3. assignment is the deciding experiment
  4. mutation alone proves only sharing
  5. a copied reference is still by value

basics

~20 s

Call by value copies the argument's value into a fresh parameter slot, so assigning to the parameter cannot reach the caller. Call by reference copies the location of the caller's variable, so assigning to the parameter replaces the caller's variable.

solid answer

~50 s

The modes differ in one thing: what the call puts in the parameter's slot. **Call by value** copies the argument's value into a new slot in the callee's frame, so the parameter is a separate variable and an assignment to it dies with the call. **Call by reference** copies the *location* of the caller's variable into the slot, making the parameter a second name for that storage, so an assignment to it is visible to the caller the instant it happens. Mutation is not the test. If a variable holds a reference to a record and that reference is copied by value, the routine can still fill the record the caller sees — two routes to one record — but it cannot replace the caller's variable. The discriminating experiment is assigning a whole new record to the parameter and asking what the caller holds afterwards.

code

pseudocode · 8 lines
pseudocode
procedure fill(record)
    record.temperature = 21     // lands on the shared record
    record = makeRecord()       // rebinds this slot only
    record.temperature = 99     // written into the new record

r = makeRecord()
fill(r)
print r.temperature             // 21 - the rebind never reached the caller

go deeper

for a junior

Recall the one-line contrast: a value copied into a new slot versus the caller's storage location copied into it. Be ready to say what an assignment to the parameter does to the caller's variable in each case.

for a middle

Explain why mutating a field through a parameter proves only that two names reach one record. Walk the experiment that actually separates the modes: assign a fresh record to the parameter and report what the caller holds.

for a senior

Show the design consequence. A by-reference parameter grants a routine permission to replace the caller's variable, not just to fill a record, and it introduces aliasing that later readers must reason about at every write.

for a principal

Frame it as an interface policy. Decide when your codebase lets a callee replace a caller's variable at all, versus requiring results to be returned, and be able to argue the cost of aliasing against the cost of copying wide records.

A **parameter mode** is the rule that connects an argument written at the call site to the parameter named inside the routine. Take a concrete setting: a device driver calls a telemetry ingest routine once per sensor reading and hands it a record to fill. The argument is the driver's variable; the parameter is the routine's. What the call *copies into the parameter's slot* is the whole of the difference between the modes, and naming it settles every "is this pass by reference?" argument in one sentence. ## Call by value Under **call by value** the call evaluates the argument and copies the resulting **value** into a fresh slot in the callee's activation record. The parameter is a brand-new variable that merely begins life holding the same value. - Assigning to the parameter writes into that fresh slot and nowhere else. The caller's variable is untouched before, during and after the call. - The call pays for however large the value is. Copying a wide record costs more than copying a small scalar, but the mode is the same in both cases — nothing about call by value restricts it to small values. - The routine cannot replace the caller's variable. If it has a result to deliver, it returns one. ## Call by reference Under **call by reference** the call does not copy the argument's value at all. It copies the **location** of the caller's variable — the storage that variable names — into the parameter slot. The parameter becomes a second name for one piece of storage. - Assigning to the parameter assigns to the caller's variable, and the caller sees it at the moment of the assignment, not at the return. - Only something that *has* a location can be passed this way. A computed expression must be given temporary storage first. - Two parameters, or a parameter and a variable the routine can already reach, may end up naming the same storage. That is **aliasing**, and it is the standing cost of the mode: a write through one name changes what the other name reads. ## Where the confusion actually comes from Most of the argument is really about a third arrangement that is *neither* mode: **a value that happens to be a reference, passed by value**. The driver's variable does not store the record; it stores a reference to the record. The call copies that reference. Now two independent slots hold two copies of one reference, and both lead to the same record. | What the call copies into the slot | Assigning to the parameter | Mutating what the parameter leads to | |---|---|---| | the value itself | caller unaffected | nothing is shared to mutate | | a reference value, copied | caller unaffected — only the copy is rebound | caller sees it — one record, two routes | | the location of the caller's variable | the caller's variable is replaced | caller sees it | Rows two and three are indistinguishable as long as the routine only ever **mutates**. They separate the moment the routine **assigns a whole new record to the parameter**: with a copied reference the caller still holds the old record, while under call by reference the caller's variable now holds the new one. That assignment is the experiment that decides it, and it is what an interviewer is listening for when the question comes up. Languages differ in what they offer here. Some provide only the copy-the-value mode and obtain all shared mutation through reference values; others provide an explicit by-reference mode declared on the parameter or requested at the call site. The mechanism to name is identical either way — a slot received a value, or a slot received a location. ## Two further modes, same question The classical list has two more entries. **Copy-restore** copies the value in at the call and copies the parameter's final value back out at the return, so the caller sees one update, late. **Call by name** substitutes the argument expression itself and re-evaluates it at every use. Both are still answered by "what did the call copy?" — a value copied twice, or an unevaluated expression. ## How to answer it cleanly 1. Say what the parameter slot received: a value, or a location. 2. Say what an **assignment to the parameter** does to the caller's variable. That is the property the mode actually governs. 3. Only then discuss mutation, which is a property of the thing referred to, not of the mode — and say so explicitly, because conflating the two is the mistake the question exists to catch. The design consequence follows from step 1. If you want a routine to fill a record for its caller, you either hand it a route to a record that already exists, or you have it return a record. Reaching for a by-reference parameter is a decision to let the routine replace the caller's variable — a larger permission than "fill this in", and one worth granting deliberately.

  • If a language offers only call by value, how can a routine still hand a filled record back to its caller?
    Three ways, all of them still by value. It can return the record as the call's result. It can be handed a reference value that leads to storage the caller already owns and write through it. Or the caller can pass a location explicitly, as a value that denotes storage, and the routine writes through that. In each case the slot received a value; only what the value denotes differs.
  • Does passing by reference avoid copying, and is it therefore always cheaper?
    It copies a location instead of the value, which is a win for a wide record and a wash or a loss for a small one. Every access through the parameter then costs an indirection, and the mode admits aliasing, so the compiler can assume less about what a write touches. Cheaper at the call, potentially more expensive inside it.
  • What single experiment distinguishes call by reference from a reference value passed by value?
    Assign a whole new record to the parameter, then look at the caller's variable. Under call by reference the caller's variable now holds the new record. With a copied reference the caller still holds the old one, because only the callee's own slot was rebound. Mutating a field answers nothing — both arrangements show it.

Handing someone a photocopy of a page is call by value: whatever they scribble on it, your page is unchanged. Handing them a copy of the map to your filing drawer is different again — they can rearrange what is in the drawer, but tearing up their copy of the map leaves your drawer, and your own map, exactly where they were.

saying these in an interview costs you the question

  • Says the caller saw a change, so it must be by reference
  • Calls every record argument by reference because the record is shared
  • Believes call by value applies only to small scalars, never to records
  • Assumes by reference avoids all copying and is therefore always cheaper
  • Says assigning a fresh record to the parameter always reaches the caller