Why Does Call-By-Value Example Not Modify Input Parameter

Is it a good practice to change arguments in Java

It's considered bad practice in general, though some people overlook it as you can see in the other answers.

For parameters like primitives that are directly passed in by value, there is no advantage in overriding the original variable. In this case you should make a copy as suggested by @João.

For parameters whose reference is passed in by value (objects), if you modify the handle to point to a different object, that is downright confusing. It's all the more important because modifying the contents of an object passed as a parameter will modify the original object too.

If you replace the object referred to by the handle, and then modify its contents, the object referred to by the original reference in the caller will not be replaced, but someone reading the code might expect it to be.

Whereas if you don't replace the object, and modify the contents, the method calling your method might not expect this change. This category generally comes under security-related bad practices.

Why does assignment to parameter not change object?

The observed behavior has nothing to do with Value types vs Reference types - it has to do with the Evaluation of Strategy (or "calling conventions") when invoking a method.

Without ref/out, C# is always Call by Value1, which means re-assignments to parameters do not affect the caller bindings. As such, the re-assignment to the x parameter is independent of the argument value (or source of such value) - it doesn't matter if it's a Value type or a Reference type.

See Reference type still needs pass by ref? (on why caller does not see parameter re-assignment):

Everything is passed by value in C#. However, when you pass a reference type, the reference itself is being passed by value, i.e., a copy of the original reference is passed. So, you can change the state of object that the reference copy points to, but if you assign a new value to the reference [parameter] you are only changing what the [local variable] copy points to, not the original reference [in the argument expression].

And Passing reference type in C# (on why ref is not needed to mutate Reference types)

I.e. the address of the object is passed by value, but the address to the object and the object is the same. So when you call your method, the VM copies the reference; you're just changing a copy.


1 For references types, the phrasing "Call By Value [of the Reference]" or "Call by [Reference] Value" may help clear up the issue. Eric Lippert has written a popular article The Truth about Value Types which encourages treating reference values as a distinct concept from References (or instances of Reference types).

Why can a function modify some arguments as perceived by the caller, but not others?

Some answers contain the word "copy" in the context of a function call. I find it confusing.

Python doesn't copy objects you pass during a function call ever.

Function parameters are names. When you call a function, Python binds these parameters to whatever objects you pass (via names in a caller scope).

Objects can be mutable (like lists) or immutable (like integers and strings in Python). A mutable object you can change. You can't change a name, you just can bind it to another object.

Your example is not about scopes or namespaces, it is about naming and binding and mutability of an object in Python.

def f(n, x): # these `n`, `x` have nothing to do with `n` and `x` from main()
n = 2 # put `n` label on `2` balloon
x.append(4) # call `append` method of whatever object `x` is referring to.
print('In f():', n, x)
x = [] # put `x` label on `[]` ballon
# x = [] has no effect on the original list that is passed into the function

Here are nice pictures on the difference between variables in other languages and names in Python.

Seperating a Funciton From int main Issues (Beginner)

#include "iostream"

int getUserNumber () {
int input;
std::cout << "Please input an integer: ";
std::cin >> input;
return input;
}

int main () {
std::cout << "testing getUserNumber function\n";
int a = getUserNumber();
std::cout << "\n" << a << "\n";
return 0;
}

Linked duplicate questions does explain why it works this way. They are technically correct but can be a bit overwhelming. Let me try to explain.

When designing a function, you have to ask yourselves the following questions

  1. What does the function needs from its caller? caller is the calling function, i.e. main.
  2. What does the function gives back?

For example, consider a function called printANumber

  1. It needs to know what to print,
  2. It has nothing to return to its caller (main function).

So, the signature is

void printANumber(int number) // void here means nothing.

Now, getUserNumber,

  1. does not need anything from its caller ( it gets the number from the user via console. Not via main function). So it should not have any parameters.
  2. And it should give back the number it got from the user to the main function, which can do anything with it. Like print it, add it, save it etc,

So the signature should be:

int getUserNumber()

If function needs something and gives back something you have both parameters and return value. E.g.

int add(int number1, int number2) // return value is the sum of both numbers

Why does a function operates on the copy of the actual parameters?

Ok this is just the convention that C-programming language chose. C++ inherited it from C.

You gave two different but somewhat related examples. I am going to address them separately.

For the second example:
When you declare a variable int a for example - machine need to store a value somewhere. That is there is certain amount of RAM allocated to store a value that can be interpreted as int. on x86, 32bit machine - this should be 32bit / 4 bytes of memory.

When a function is called with arguments - values must be passed to a function. THat is some memory must be allocated to store this values. C and C++ chose to copy values by default. That is the fist thing that happens when a function is called - some memory allocated. for its arguments and values are copied to that new memory. This works nice for ints as they can be stored in CPU registers - which are limited in size. If you want to modify values - you need to take an address of the memory where a value is stored - pass that address to a function. Note you have at copied the address. But having address - pointer - allows you to change values stored at that address.


// Copy two integers
void interChange(int a, int b) {
int tmp;
tmp = a;
a = b;
b = tmp;
}

void interChangePtr(int* a, int* b) {
int tmp;
tmp = *a;
a* = *b;
b* = tmp;
}

int main() {
int a = 5, b = 3;
interChange(a, b);
// a=5, b=3.

interChangePtr(&a, &b);
// a=3, b=5

return 0;
}

As for your second example - this is another convention that C chose. When you type:

int main() {
int arr[25]; // Allocates memory on the stack for 25 integers
...

Declaring an array (in C-style) informs the compiler that you'd like it to allocate memory for your array on the stack. An array is just a continues chunk of memory. So you can take a pointer to it and modify values using that pointer. And in C if you type arr - this is the pointer to memory allocated for you on the stack. So when you call a function void function(int arr[]) as function(arr) - this actually passes a pointer to your array not the actual block of memory.
The reason for these conventions - is performance. It is faster to pass a single pointer to an array then to allocate a new array and copy data over.

Hope that gives you some pointer to reseach the topic further.



Related Topics



Leave a reply



Submit