4

In the following code, I pass to the function a pointer to *example[10]; or the entire array?

#include <stdio.h>

void function (int **)

int main ()
{
    int *example[10];
    function(example);
    return 0;
}

void function (int *example[10])
{
    /* ... */
    return;
}

The same question for the following code:

#include <stdio.h>

struct example
{
    int ex;
};

void function (struct example *)

int main ()
{
    struct example *e;
    function(e);
    return 0;
}

void function (struct example *e)
{
    /* ... */
    return;
}
Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
ᴜsᴇʀ
  • 1,109
  • 2
  • 9
  • 23
  • 2
    There is not way to pass anything by-reference in C. You can simulate by-refereance by passing a pointer-to-pointer. – Tobias Wärre Jan 16 '14 at 14:57
  • 2
    Yes, a "simulation of by-refereance" `by passing a pointer-to-pointer`. For example it's better to pass a struct with this "simulation" and not the entire struct, right? – ᴜsᴇʀ Jan 16 '14 at 15:25
  • 1
    There is no need for extra pointers to pass a struct when a simple pointer will do fine. Note that any changes to anything passed as non-pointer will be local to that function, while if passed by pointer and the result will be seen even after that function returns. There's a can of worms here, so experiment a bit if you want to, but do it with simple experiments so that it is easy to figure out what went wrong. – Tobias Wärre Jan 16 '14 at 19:29
  • 1
    _@TobiasWärre_. I'm asking these questions because I'm just experimenting ;) And I have not yet figured out why _"There is no need for extra pointers to pass a struct!"_ I thought I had understood that with the extra pointer I can modify the struct into the function (and it changes in `main()` too), and without extra pointer I can modify only locally! – ᴜsᴇʀ Jan 17 '14 at 11:48
  • 1
    If you have simply one pointer to a struct, you can still make changes to the struct which is visible outside that function. However, if you make changes to the _pointer_ to that struct, then the change is local. That is why, to simulate the by-reference, you need to pass a pointer to the pointer that you want to modify. (Hance, you can modify the object, in this case a pointer, that the pointer points to). – Tobias Wärre Jan 21 '14 at 09:23

2 Answers2

5

C only has call by value, so (formally) all values are passed to functions by value.

However, arrays are handled a bit differently in C compared to other data, so in your first example, the array example is converted to a pointer to its first element, and then that pointer is passed (by value) to the function.

In your second example, e is a pointer to struct (but it is never set to point anywhere), and you pass that pointer (by value) to the function.

In both cases, the parameter passing is done by value, but when you pass a pointer to a variable, the called function can make changes to the original variable.

Thomas Padron-McCarthy
  • 27,232
  • 8
  • 51
  • 75
  • @haccks: How do you figure that is more precise? The C standard uses “converted”, not “decayed”, in C 2011 6.3.2.1 3. – Eric Postpischil Jan 16 '14 at 15:06
  • @EricPostpischil; By K&R C. Forgot about the standard quote. Sorry for that. – haccks Jan 16 '14 at 15:07
  • 1
    _@Thomas Padron-McCarthy._ _"In both cases, the parameter passing is done by value, but when you pass a pointer to a variable, the called function can make changes to the original variable."_ But in the second code I can't make changes to the original variable! To make change I must do this: `function(&e);` and `void function (struct example **e)`, right? – ᴜsᴇʀ Jan 16 '14 at 15:21
  • 1
    @user "But in the second code I can't make changes to the original variable!" In the second case you do not have an original variable, because your are passing a pointer to a function parameter of type pointer, and the pointer that you are passing is uninitialized. If you do this `struct example se; struct example *e = &se; function(e)`, you would be able to make changes to the original variable, which in this case would be `se`, not `e`. – Sergey Kalinichenko Jan 16 '14 at 15:27
  • _@Thomas Padron-McCarthy._ I not understand. Look for example here: http://stackoverflow.com/a/21070007/3185964 – ᴜsᴇʀ Jan 16 '14 at 15:29
  • @user Yes, that's the same deal: if you want to modify a variable, you pass a pointer to it. If your variable is a pointer, pass a pointer to a pointer; if your variable is a pointer to a pointer, pass a pointer to a pointer to a pointer, and so on. – Sergey Kalinichenko Jan 16 '14 at 15:37
5

In C all parameters are passed by value, including pointers. In case of passing arrays, an array "decays" to a pointer to its initial element.

Your first function passes a pointer to a block of ten unitialized pointers to int. This may be useful, because function(int**) can change pointers inside the array to valid pointers. For example, this is allowed:

void function (int *example[10])
{
    for (int i = 0 ; i != 10 ; i++) {
        // Allocate a "triangular" array
        example[i] = malloc((i+1)*sizeof(int));
    }
}

(of course the caller is now responsible for all this allocated memory)

Your second function passes an uninitialized pointer. This is entirely useless, because function(struct example *e) can neither assign nor dereference this pointer legally.

This would be illegal:

void function (struct example *e)
{
    e->ex = 123; // UNDEFINED BEHAVIOR! e is uninitialized
}

This would not have an effect on the value of e in the caller:

void function (struct example *e)
{
    e = malloc(sizeof(struct example)); // Legal, but useless to the caller
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 2
    But if it was `function (&e);` and `void function (struct example **e)`, than `*e = malloc(sizeof(struct example));` it's legal and useful, right? – ᴜsᴇʀ Jan 16 '14 at 15:45
  • 1
    Then if `e = malloc(sizeof(struct example));` in `void function (struct example *e)` is _"useless to the caller"_, are there cases where you need to use it? – ᴜsᴇʀ Jan 16 '14 at 19:21
  • 2
    @user Passing an uninitialized pointer to a function is identical to declaring an uninitialized local variable of the same type. In fact, the only difference between local variables and parameters is that the later get initialized by the caller, while the former get initialized inside the function itself. Calling `e = malloc(sizeof(struct example));` would let you use `e` as if it were a regular local variable, without causing a crash. – Sergey Kalinichenko Jan 16 '14 at 19:26
  • 2
    Thank you, I understand. And if in the `main()` I declared `struct example *e = NULL;` is that cosidered initialized or not? – ᴜsᴇʀ Jan 16 '14 at 19:30
  • 1
    @user Yes, setting that pointer to `NULL` would initialize it. It wouldn't do much as far as making it more useful, because your `function(struct example *)` can neither dereference nor re-assign it, but at least it would be able to test it for `NULL`, and legally verify that it is not pointing to a valid block of memory. – Sergey Kalinichenko Jan 16 '14 at 19:41