0

In the example, adapted from a book, I want to fully understand how passing and array by reference really works. From playing with the below code in Code Blocks and some reading here and there, I get that passing by value doesn't change the initial variable's value ( the example_pass_by_value). As for the other two variables whose adresses are passed by reference to the change function, I only understood the example_pass_by_reference case like this : when calling the function like this "change (&example_pass_by_reference), you actually put and "=" sign between the formal parameter "by_reference" ( this being declared as pointer in the change function definition) and the address of "example_pass_by_reference" variable whic is passed to the change function ( ex: by_reference = 0x6644fe, or better by_reference = &example_pass_by_reference); The thing i don't understand is how the "=" is put between the address of array in the calling of function "change(array, ....) and the formal parameter "int arr[]" from definition of the function "change(int arr[], ...)", since int arr[] is an array, not a pointer, to be able to receive the address of variable "array".( like int arr[] = array, or int arr[] = 0x7856ff, for example); Thank you in advance and sorry for the subsequent errors in my code or language.

#include <stdio.h>
#include <string.h>

main()
{
    int array[3] = {1,2,3};
    int example_pass_by_value = 5;
    int example_pass_by_reference = 6;

    change(array, example_pass_by_value, &example_pass_by_reference);

    printf("Back in main(), the array[1] is now %d.\n", array[1]);
    printf("Back in main(), the example_pass_by_value is now %d.\n, unchanged", example_pass_by_value);
    printf("Back in main(), the example_pass_by_reference is now %d.\n", example_pass_by_reference);
    return(0); 
}

/******************************************************************/

change(int arr[], int by_value, int *by_reference)  
{
    // Changes made to all three variables; 
      arr[1] = 10;
      by_value = 100;
      *by_reference = 1000;
      return; 
}
painkiller
  • 149
  • 6
  • 2
    C does not pass anything by reference, it doesn't have references. It passes everything by value, or by pointer, which is a type of value. Are you asking "How do C arrays decay into pointers when used as arguments?" – tadman Feb 16 '20 at 23:13
  • I'm asking how does the address of the "array" variable is passed by reference when the change function is called, if in the definition of the change function, the equivalent formal parameter of the actual parameter ( the "array" variable) is yet another array ( the int arr[] variable) , and not a pointer, thus unable to receive an address of another variable. – painkiller Feb 16 '20 at 23:20
  • Again, **C does not pass anything by reference**. It's all values, and those values *can* be pointers. Do those act like references? **Only if de-referenced**. The way the array gets altered is because `int arr[]` decays into `int* arr` when used. it turns into a pointer. De-referencing that alters the original. Note, however, that alterations to `arr` itself do not back-propagate. `arr = NULL` changes nothing in the caller. – tadman Feb 16 '20 at 23:22
  • This is likely a duplicate of [this question](https://stackoverflow.com/questions/1461432/what-is-array-decaying). – tadman Feb 16 '20 at 23:23
  • 2
    @tadman: “Reference” is an English word meaning to mention or allude to something. “Pass by reference” means to provide something by some handle to it, rather than by value. Passing a pointer is such an action. This meaning was in use before C++ existed, and the fact that C++ built references into the language as a primary type does not nullify prior use of the word. The `*` operator in C is a dereference operator. The C standard says the type a pointer is derived from is the *referenced* type (C 2018 6.2.5 20). So stop with the nonsense; any way of referring to an object is a reference. – Eric Postpischil Feb 16 '20 at 23:33
  • So, by calling the function like : change( array), I'm sending the adress of the variable “array” to the called function's formal parameter (int arr[])), which in the function's definition is not an array anymore, but “decays” to a pointer(and thus receiving the address of the array variable)? If this is true, then my only question is this: due to what property or theoretical base does the array " int arr[]” decays into a pointer, when used as a formal parameter in the definition of the change function, in my example and be able to receive the address of the array variable? – painkiller Feb 16 '20 at 23:35
  • 1
    "since int arr[] is an array, not a pointer". You are mistaken. **It is a pointer**. The `[]` notation is misleading. It does **not** denote an array. See e.g. [this](https://stackoverflow.com/questions/18833174/can-i-use-arrays-as-a-function-parameter-in-c99) – n. m. could be an AI Feb 16 '20 at 23:35
  • 1
    @EricPostpischil There's layers to that definition. In the C world you can pass a "reference" to something in pointer form, but that reference itself is passed by value. Perhaps I should rephrase to "C does not implicitly pass anything by reference, you must explicitly pass a pointer". – tadman Feb 16 '20 at 23:37
  • @n. 'pronouns' m, how is arr [] not a array? I don't understand. Due to what definition is it a pointer? If you can explain it to me in detail, that would be great. – painkiller Feb 16 '20 at 23:39
  • 1
    @tadmin: Not quite true, when you pass an array, you do not explicitly pass a pointer. It is implicit. It is true to say C does not have implicit references as a primary type, where passing `x` actually passes the address of `x` in a hidden way. Regardless, a pointer **is** a reference, the same way “George Washington” is a reference to George Washington. If it were not a reference, we could not dereference it with the dereference operator. When students refer to passing by reference in C, we are not helping them by saying there is no pass by reference in C… – Eric Postpischil Feb 16 '20 at 23:42
  • The formal parameter does not decay to a pointer, rather, its type is *adjusted* (rewritten) to be a pointer instead of an array. An *actual* array parameter decays to a pointer (or, more broadly, an array in a value context decays to a pointer). Both of these things happen because the C language definition says so, not because of theoretical anything. – n. m. could be an AI Feb 16 '20 at 23:42
  • … You could tell them that C++ has this nifty feature that lets them pass by reference implicitly, and people call that pass by reference, but what use is that in the context of C? Just stop introducing C++ concepts in C discussions. They are out of place. – Eric Postpischil Feb 16 '20 at 23:42
  • The exact words of the C language definition that are responsible for this phenomenon can be found by following the link in my first comment. I cannot explain stuff in detail in comments, they are not designed for that. If you have a question after following the link, ask a question. – n. m. could be an AI Feb 16 '20 at 23:49
  • @n. 'pronouns' m, by your last comment, you're saying that, in my example, the actual parameter used ( the array in change (array) is decaying from an array variable to a pointer variable = an address (ex. 0x6644ff)? Then, after this address is passed to the formal parameter "int arr[]", this one is also no longer an array, but it is adjusted to a pointer, in order to be able to receive the passed address (0x6644ff)? If that's the case, and you're saying this happens because of the C language definition, can you point me to some official C language definitions that says all these? – painkiller Feb 16 '20 at 23:52

2 Answers2

2

In this function declaration:

void change(int arr[], int by_value, int *by_reference)

C does not support passing arrays as arguments. When you declare a parameter to be an array, the compiler automatically adjusts it to be a pointer. So this declaration is as if it were:

void change(int *arr, int by_value, int *by_reference)

(Note, I added an int before the function declaration. You should always write a return type, such as int or void, before a function that is being declared. Your compiler may insert a default type of int, but that is an old C feature and should not be used anymore.)

To match the automatic adjustment in the declaration, C also automatically converts arrays in arguments (and most other expressions). In the function call:

change(array, example_pass_by_value, &example_pass_by_reference);

The array array is automatically converted to a pointer to its first element, as if it were:

change(&array[0], example_pass_by_value, &example_pass_by_reference);

Now let us look at the code inside the function. This statement:

arr[1] = 10;

says to take the pointer arr, which has the address of the first element of array, add 1 to it, and use the element there. One element beyond array[0] is array[1], so arr[1] refers to array[1]. This statement assigns 10 to it. So array[1] becomes ten. Note that the array was not passed by value, because C automatically converted it to a pointer. The pointer was passed by value, and the pointer gives us a reference to elements of the array, and arr[1] uses that reference to access elements of the array.

This statement:

by_value = 100;

sets the parameter by_value to 100. Since it is only changing the parameter, it does not change the argument example_pass_by_value in the main function.

This statement:

*by_reference = 1000;

uses the pointer by_reference to access an int in memory. The * operator says to use the pointer to refer to the object it points to. So this statement assigns the value 1000 to the int that by_reference points to. The pointer by_reference was passed by value, and it provides a reference to example_pass_by_reference, because the address of example_pass_by_reference was passed as the argument.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • where can I find your explanation in the C language definitions ? : "C does not support passing arrays as arguments. When you declare a parameter to be an array, the compiler automatically adjusts it to be a pointer. So this declaration is as if it were: void change(int *arr, int by_value, int *by_reference)" – painkiller Feb 17 '20 at 00:06
  • 1
    @painkiller: That is in C 2018 6.7.6.3 7: “A declaration of a parameter as "array of *type*" shall be adjusted to "qualified pointer to *type*", where the type qualifiers (if any) are those specified within the `[` and `]` of the array type derivation…” – Eric Postpischil Feb 17 '20 at 00:33
1

For starters a typical mistake of whose who do not know C is the statement like that

C does not pass anything by reference, it doesn't have references

C does not have references in the sense as C++ has but it has a mechanism of passing by reference.

You shall not mix the terminology and notions of different languages and shall use the terminology defined in the concrete language.

In C pointers play the role of references. From the C Standard

— A pointer type may be derived from a function type or an object type, called the referenced type. A pointer type describes an object whose value provides a reference to an entity of the referenced type. A pointer type derived from the referenced type T is sometimes called ‘‘pointer to T’’. The construction of a pointer type from a referenced type is called ‘‘pointer type derivation’’. A pointer type is a complete object type.

So to pass by reference in C means passing an object through a pointer to the object.

As for your question then arrays used in expressions (with rare exceptions as for example using in the sizeof operator) are implicitly converted to pointers to their first elements.

You can imagine the following function call

void f( int a[] );

//...
int a[] = { 1, 2, 3, 4, 6 };

f( a );

like

void f( int a[] );

//...
int a[] = { 1, 2, 3, 4, 6 };

int *tmp = a;    
f( tmp );

or that is the same

void f( int a[] );

//...
int a[] = { 1, 2, 3, 4, 6 };

int *tmp = &a[0];    
f( tmp );

On the other hand, a function parameter having an array type is implicitly adjusted by the compiler to pointer to the type of element of the array.

So for example these two function declarations are equivalent and declare the same one function

void f( int a[] );
void f( int *a );

So passing an array to a function you are passing pointer to its first element. It means that in fact all elements of the array are passed by reference. Using the pointer arithmetic you can change any element of the array pointed to by the pointer.

For example this statement

arr[1] = 10;

is evaluated by the compiler like

*( arr + 1 ) = 10;

where arr is a pointer to the first element of an array.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335