Call by value resolves the argument and passes it's value to the subroutine/function (so, the function gets a COPY of the argument)Never got call-by-value vs call-by-reference, even though that was covered in my curriculum.
Call by reference, passes a reference/pointer to it to the subroutine/function.
The latter is particularly useful for passing large objects/structs because a pointer is small -- it fits in a processor register whereas the thing that it references may be kilobytes or larger.
With a reference to the parameter, the called function can alter the original! When the value of the original is passed, it's just a copy -- so the original is "safe".
Additionally, in a multithreaded environment (or, in any case where some other actor can access the original while the function is also accessing it), there is a risk in call by reference that one or the other could make changes that corrupt the other's view of the parameter -- because there is nothing to ensure atomic access for either!
E.g., I pass a pointer to a string -- "31 Oct 2024" -- to a function. Before the function gets to act on it. another thread starts to change it to reflect the NEW date -- "01 Nov 2024". But, only manages to change the first character before the function gets its chance to peek at it.
When the function accesses it, the string is "01 Oct 2024" -- which is almost certainly NOT what the function's caller expected it to be when the function was invoked. It's also neither of the two values that seem possible (31 Oct or 01 Nov)
Similarly, if the function changes the string before that other thread gets a chance to look at it, the thread will see some/all of the changes instead of the original.
[While a silly example, the point being you can't predict the actions/interactions of either actor because they are sharing an object (the string) without any mechanism -- e.g., a mutex -- to arbitrate their accesses to it. And, the problem is probably not going to manifest itself reliably so you'll be troubleshooting an intermittent]
Imagine if the passed (shared!) object is "substantial" -- like a frame of video on which you are expecting the function to perform "scene analysis". While the function is "looking at the picture", something is reusing that "buffer" to store the next image. So, it is likely that the function will see part of the previous image and part of the new image -- and be unaware that the scene is in an inconsistent state (neither previous nor new).
How will this manifest? Will other actions by the program be confused because of the misanalyzed scene results? Will the user be able to identify this as the cause of the problem he is experiencing? How many kilobucks will you spend trying to figure out that this is the cause of the "bad behavior" before you can actually fix it?