0

I am learning some of the basics of C, and am currently stepping my way through arrays and more specifically how passing by reference works. When the below code is run it returns 10 22. When I read through the code however, based on the last command it seems as though the variable a should return 22 instead of 10 (meaning the full output would be 22 22 instead of 10 22). Why would the variable a not update to 22 in this code?

#include <stdio.h>

void set_array(int array[4]);
void set_int(int x);

int main(void)
{
    int a = 10;
    int b[4] = { 0, 1, 2, 3 };
    set_int(a);
    set_array(b);
    printf("%d %d\n", a, b[0]);
}

void set_array(int array[4])
{
    array[0] = 22;
}

void set_int(int x)
{
    x = 22;
}
1
  • 1
    C does not have references. set_int takes a plain old value. Commented Jun 28, 2020 at 19:21

4 Answers 4

3

Arrays are [loosely] "pass by reference". Actually, the array "decays" into an int *.

But, scalars are "pass by value".

In set_int, you set the function scoped copy of x but do not return it to the caller.

Here's the refactored code, with a "call by reference" example:

#include <stdio.h>

void
set_array(int array[4])
{
    array[0] = 22;
}

int
set_int(int x)
{
    x = 22;

    return x;
}

void
set_int_byptr(int *x)
{
    *x = 37;
}

int
main(void)
{
    int a = 10;
    int b[4] = { 0, 1, 2, 3 };
    int c = 4;

#if 0
    set_int(a);
#else
    a = set_int(a);
#endif

    set_array(b);
    set_int_byptr(&c);

    printf("a=%d b=%d c=%d\n", a, b[0], c);

    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

@snr Although it could be done [with a slight change]: set_array(&a) (needs the & because a is a scalar, it's cleaner [IMO] to use a separate function that takes an explicit int *. The set_array [in the future] might get extended to be more useful: void set_array(int array[4],int idx) { array[idx] = 22; }. In that case, using set_array on the scalar would become confusing
1

In C if you want to modify variable passed to function you need to pass the pointer to it:

examples:

int setval(int *obj, int value)
{
     *obj = val;
     return val;
}

void usage()
{
     int x;
     setval(&x, 22);
}

void *setpointer(void **ptr, size_t size)
{
     *ptr = malloc(size);
     return *ptr;
}

void usage1()
{
    int *array;
    setpointer(&array, 200*sizeof(*array));
}

Comments

1

First we need to get this out of the way, because I honestly believe it will make things less confusing - C does not pass any function arguments by reference, ever. C passes all function arguments by value. Sometimes, those values are pointers. This is not the same thing as pass-by-reference.

Among other things, pass-by-value means that any changes to a formal parameter are not reflected in the actual parameter. In your set_int function, x is a distinct object from a, and any changes to x do not affect a.

If we want a function to modify the value in a parameter, we must pass a pointer to that parameter:

void set_int( int *x )
{
  *x = 22; // writes a new value to the thing x points to
}

int main( void )
{
  int a = 10;
  set_int( &a ); // foo writes a new value to a
  return 0;
}

In the above code, we want the function set_int to update the variable a, so we must pass a pointer to a to the function.

 x == &a // int * == int *
*x ==  a // int   == int

Thus, writing a new value to the expression *x in set_int is the same as writing a new value to a in main. Any change to x itself is local to set_int.

Things get confusing when we add arrays to the mix. An array is not a pointer; however, unless it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T" and the value of the expression will be the address of the first element.

When you call set_array(b), the expression b "decays" from type "4-element array of int" (int [4]) to type "pointer to int" (int *), and the value of the expression is the same as &b[0].

Here's another confusing thing - in a function parameter declaration, array declarations of type T a[N] and T a[] are interpreted as T *a - a is a pointer to T, not an array of T. So your prototype

void set_array(int b[4])

is interpreted as

void set_array(int *b)

and what it receives is a pointer to the first element. As a practical matter, this means that any changes to array[i] in set_array are reflected in b, but this is fallout from how C specifically treats array expressions, not a difference in parameter passing mechanisms. The argument is still being passed by value, it's just that the argument is a pointer value that's the result of a well-defined conversion operation on array expressions.

Comments

0

You are doing 2 things over here:

1) Pass by value: the function set_int(), its parameter is passed simply, without any address, which means it is pass by value, and any change made by this function set_int() will not be reflected in the calling function.

2) Pass by reference: However, in the case of set_array(b), you are passing the array to the called function, and its base address will be passed (Means address of first element of b, that is &b[0]), hence this is pass by reference and any change is made to this value will be reflected in the calling function

which is the reason 22 is updated for b, but 22 didn't get update for a

9 Comments

there is no pass by reference in C, only by pointer
@P__J__: The address of something is a reference to it. Passing the address of an object, whether it is taken explicitly with & or implicitly with automatic conversion of an array, is passing by reference. This terminology was used as such historically. The fact that C++ came along and made something in the language it called a “reference” did not take away, negate, nullify, abolish, or invalidate the former uses of the word “reference” in other languages and contexts. Writing the name of an an array as an argument to a function call in C is passing by reference.
@EricPostpischil strictly speaking C has only pass by value. The "pass by reference" is just pass pointer by value. So both terms in C are not valid.
@P__J__: As I explained, C does have call by reference. It is merely done manually, not as an automatic feature. Except for the fact that it is automatic for arrays. Nobody said C does not have call by reference until C++ made a type call a reference and people started confusing C++ terminology with C terminology. If you do not want to use the term, fine, but there is no call to be correcting others who do use it. Their use of the phrase is correct.
@EricPostpischil Where in the C standard you see call by reference. Except for the fact that it is automatic for arrays. it is not pass by reference. arrays decay to pointers and pointer is passed by value.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.