0

In my program, I am a passing a pointer to a function. In that function, I am making the passed pointer to point to a location what another pointer points. When returned from function it no longer points to its new location, instead it points to its original location. As I am passing by call by reference, it should point to its new location. Why is this happening?

// a program to show behavior of call by reference

#include <stdio.h>
#include <stdlib.h>

void ptrAllocation(int *p)
{
    int k = 10 ;
    int *t = &k;
    p = t ;
    printf("\np now points : %d",*p);
}

int main()
{
    int i = 5 ;
    int *a = &i;
    ptrAllocation(a);
    printf("\na now points : %d",*a);
}

Output:

p now points : 10
a now points : 5

I know the problem can be solved if I make a function like:

void ptrAllocation(int **p)
{
    int k = 10 ;
    int *t = &k;
    *p = t ;
    printf("\np now points : %d",**p);
}

But I am not getting the clear picture of what is happening exactly in the program from the point of view of pointers, location, stack?

MY PROBLEM: Is that the pointer k points to whatever pointer t points in the function ptrAllocation, but as the function returns, there no longer exists pointer t, hence pointer p points to its original location. Is it not the case that when assigning a pointer to a pointer like in p = t, both pointers p and k point to the same location and not that p points to t and t points to the location.

Please describe how the stack and the pointers work in the program above.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Subbu
  • 2,063
  • 4
  • 29
  • 42
  • 4
    There is a second problem with your code. The location of `k` is undefined after `ptrAllocation` returns. So even if you pass by reference, there is no guarantee that your pointer will point to a memory location with `10` in it. "undefined behavior". – Floris May 28 '13 at 02:57
  • This is a similar question with good answers: http://stackoverflow.com/questions/1659302/how-does-call-by-value-and-call-by-reference-work-in-c – jogojapan May 28 '13 at 03:00
  • make sure you read the answer(s) that specify there is no pass by reference in C – xaxxon May 28 '13 at 03:10

3 Answers3

6

You are passing by value, not by reference. What you pass by value is a pointer, but the pointer is still passed by value. You can change what it points at; you can't change the pointer in the calling function. If you wanted to do that, you'd have to pass a pointer to the pointer, as in your second code fragment.

Here's a derivative program based on your code, and its output from a 64-bit build on Mac OS X 10.8.3. I used 12 in the address printing to give uniform width pointer output on this machine; you can tune it to suit your machine.

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

static int q = 42;

static void ptrAllocation(int *p, int **z)
{
    printf("p now points at: %2d (0x%.12" PRIXPTR ")\n", *p, (uintptr_t)p);
    printf("z now points at: %2d (0x%.12" PRIXPTR ") (0x%.12" PRIXPTR ")\n", **z, (uintptr_t)*z, (uintptr_t)z);
    int k = 10;
    int *t = &k;
    *z = &q;
    p = t;
    printf("After:\n");
    printf("p now points at: %2d (0x%.12" PRIXPTR ")\n", *p, (uintptr_t)p);
    printf("z now points at: %2d (0x%.12" PRIXPTR ") (0x%.12" PRIXPTR ")\n", **z, (uintptr_t)*z, (uintptr_t)z);
}

int main(void)
{
    int i = 5;
    int j = 7;
    int *a = &i;
    int *b = &j;
    printf("Before:\n");
    printf("a now points at: %2d (0x%.12" PRIXPTR ")\n", *a, (uintptr_t)a);
    printf("b now points at: %2d (0x%.12" PRIXPTR ")\n", *b, (uintptr_t)b);
    ptrAllocation(a, &b);
    printf("a now points at: %2d (0x%.12" PRIXPTR ")\n", *a, (uintptr_t)a);
    printf("b now points at: %2d (0x%.12" PRIXPTR ")\n", *b, (uintptr_t)b);
}

Sample output:

Before:
a now points at:  5 (0x7FFF59E1852C)
b now points at:  7 (0x7FFF59E18530)
p now points at:  5 (0x7FFF59E1852C)
z now points at:  7 (0x7FFF59E18530) (0x7FFF59E18538)
After:
p now points at: 10 (0x7FFF59E18534)
z now points at: 42 (0x000105DE8050) (0x7FFF59E18538)
a now points at:  5 (0x7FFF59E1852C)
b now points at: 42 (0x000105DE8050)

Studying the output should help you understand better what is going on. You can print more address values if you need to.


Please describe how the stack and pointers work in the program above.

I'll discuss the program I showed because the addresses are available for discussion.

The variable q is located at address 0x000105DEE8050. Inside main(), variable i is stored on the stack at memory location 0x7FFF59E1852C; the variable j is stored at memory location 0x7FFF59E18530. The variable a contains the address of i; b contains the address of j; the address of b itself is 0x7FFF59E18538; the address of a is not shown in the output.

When ptrAllocation() is called, the value of a is pushed onto the stack, and the address of b is also pushed onto the stack. It is an implementation detail which order the values are pushed.

Inside ptrAllocation(), the variable p contains a copy of the value in a in the main() function. The variable z contains the address of b in the main() function. The variable k is on the stack; the variable t contains the address of k.

The assignment *z = &q; assigns the address of q to the pointer b via the argument z; it changes what b points at by changing the value of b in the calling function — which is only possible because the address of b was passed.

The assignment p = t; changes the local variable p (which contains a copy of what is in the variable a in main()) so that it points to what t points at, which is k. Therefore, the 'After' print statements note that p points at the value 10. The value that **z points at is in q and is still 42; *z is the address of q.

On return, a in the main() code is unchanged because its address was not passed to ptrAllocation(), but b is changed because its address was passed to ptrAllocation() and ptrAllocation() modified the pointer.


As noted in the comments to the question, your second implementation of ptrAllocation() is flawed too:

void ptrAllocation(int **p)
{
    int k = 10 ;
    int *t = &k;
    *p = t ;
    printf("\np now points : %d",**p);
}

After calling this function, the pointer passed to it cannot reliably be dereferenced because *p points to a local variable which ceases to be valid once ptrAllocation() returns. You could fix this issue by making k into static int k = 10;, or by arranging for *p to point to some other int value that has a scope outside the function — a variable defined outside the function, or a dynamically allocated variable:

void ptrAllocation(int **p)
{
    static int k = 10;
    int *t = &k;
    *p = t ;
    printf("p now points : %d\n", **p);
}

Or:

void ptrAllocation(int **p)
{
    *p = malloc(sizeof(*t));
    if (*p != 0)
    {
        **p = 10;
        printf("p now points : %d\n", **p);
    }
}

Note, incidentally, how the code I've left has the newlines at the end of each printf() statement. Get into the habit of placing newlines at the end of lines of output. If you don't, you don't know when the output will appear (because it may be held up until the next time a newline is generated).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • if q is not static , then b would point to a garbage value , right ? – Subbu May 28 '13 at 03:16
  • @CodeJack: `q` was made external to all functions so that `b` would point to a valid value after `ptrAllocation()` exited — yes. Otherwise, it would be moderately difficult to find a valid value for `b` to point at. If `*z = p;` was executed before `p = t;`, then on return to `main()`, `b` would point to the same place as `a`, namely the variable `i` in `main()`. After the `ptrAllocation()` function returns, all the (non-static) variables in the function cease to be valid; the space previously reserved for them may be reused for other purposes. – Jonathan Leffler May 28 '13 at 03:27
  • Sir , actually my question was http://stackoverflow.com/questions/16705777/searching-a-node-with-balance-factor-of-2-in-an-avl-tree . In this question , I found out that `r` was printing as `NULL` because it pointed a local variable 'ptr' which got dereferenced when the function returns . Thank you – Subbu May 28 '13 at 05:12
  • Sir ,it means when we pass a value of a function to a function , a copy is created and when we pass address of a variable to a function , copy is not created instead the variable itself is passed . According to me , the later method is called 'CALL BY REFERENCE' . Then why is it said that 'there is no CALL BY REFERENCE in C only CALL BY VALUE' – Subbu May 28 '13 at 05:15
  • sir , In my second code fragment , i have not used static , still it works fine . – Subbu May 28 '13 at 05:21
  • About your second fragment: it only works fine by accident. If I wrote a function such as: `void stack_spoiler(int n) { char buffer[n]; for (int i = 0; i < n; i++) buffer[i] = i % 256; puts(&buffer[n % 256]); }` and called it between the time that `ptrAllocation()` returned and the `printf()` statement (e.g. `stack_spoiler('a');`), you'd probably see that the second fragment did not work fine any more. (I'm still working out what the prior two comments are saying.) – Jonathan Leffler May 28 '13 at 05:25
  • In [SO 16705777](http://stackoverflow.com/q/16705777), your function is `void searchForUnrequiredBalanceFactor(avlnode *n, avlnode *r)` and the body of the function contains `avlnode *ptr = n; ...; r = ptr; return;` which is entirely analogous to your function here. You can set the local variable `r` to a value, but you've not affected the variable in the calling function. You'd need `void searchForUnrequiredBalanceFactor(avlnode *n , avlnode **r)` and `*r = ptr;` to affect the calling function. – Jonathan Leffler May 28 '13 at 05:32
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/30726/discussion-between-codejack-and-jonathan-leffler) – Subbu May 28 '13 at 05:35
  • Your comment _'when we pass a value of a function to a function, a copy is created and when we pass address of a variable to a function, copy is not created instead the variable itself is passed'_ is not coherent to me, even if we change the first 'function' into 'variable'. Given `int k = 10; int *p = &k;` and a function call `somefunc(k, &k, &p);`, three values are copied onto the function's call stack: the value of the variable `k` (the integer 10), the address of the variable `k`, and the address of the pointer `p`. The same data is passed if the call is `somefunc(k, p, &p);` instead. – Jonathan Leffler May 28 '13 at 05:37
  • Oh yes, that was a 'value of a variable to a function' ..sorry – Subbu May 28 '13 at 05:41
1

The C language only implements call by value. Call by reference refers to passing (by value) a pointer p, and then using *p to manipulate what it points at (references).

If you want a function to change a pointer, you have to reference the pointer, such as by forming a pointer to pointer, and pass the reference to the function using call by value. The terms "reference" and "pointer" may be used nearly interchangeably in C — referencing is just what a pointer does.

In C++ call by reference is a language feature, but in C it's an idiom.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
1

You are changing the pointer in function and not in the main code. Therefore only the copy will be changed. In the later case you change the location of pointer so the value at that address is changed. Take it like variables, when you change the copy, nothing happens to the original but when you change the location, the variable is changed. So you have to pass the address of pointer to change it.

SureshS
  • 589
  • 8
  • 23