14

Suppose I have a function that declares and initializes two local variables – which by default have the storage duration auto. This function then calls a second function, to which it passes the addresses of these two local variables. Can this second function safely use these pointers?

A trivial programmatic example, to supplement that description:

#include <stdio.h>

int adder(int *a, int *b)
{
    return *a + *b;
}

int main()
{
    auto int a = 5;    // `auto' is redundant; included for clarity
    auto int b = 3;

    // adder() gets the addresses of two auto variables! is this an issue?
    int result = adder(&a, &b);
    printf("5 + 3 = %d\n", result);

    return 0;
}

This program works as expected, printing 5 + 3 = 8.

Usually, when I have questions about C, I turn to the standard, and this was no exception. Specifically, I checked ISO/IEC 9899, §6.2.4. It says there, in part:

4 An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration.

5 For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate. If an initialization is specified for the object, it is performed each time the declaration is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.

Reading this, I reason the following points:

  1. Variables a and b have storage duration auto, which I've made explicit using the auto keyword.

  2. Calling the adder() function corresponds to the parenthetical in clause 5, in the partial quote above. That is, entering the adder() function "suspends, but does not end," the execution of the current block (which is main()).

  3. Since the main() block is not "end[ed] in any way," storage for a and b is guaranteed. Thus, accessing them using the addresses &a and &b, even inside adder(), should be safe.

My question, then, is: am I correct in this? Or am I just getting "lucky," and accessing memory locations that, by happenstance, have not been overwritten?


P.S. I was unable to find an exact answer to this question through either Google or SO's search. If you can, mark this as a duplicate and I'll delete it.

ravron
  • 10,213
  • 2
  • 37
  • 62
  • 1
    It would be a quite sad thing if this not worked. You're not getting my upvote because I've run out of votes for today, but I like that "happenstance" wordy ;) –  Jul 22 '13 at 22:59

4 Answers4

8

Yes, it is safe and basically your assumptions are correct. The lifetime of an automatic object is from the entry in the block where it has been declared until the block terminates.

(C99, 6.2.4p5) "For such an object [...] its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way.

ouah
  • 134,166
  • 14
  • 247
  • 314
7

Your reasoning is correct for your particular function call chain, and you have read and quoted the relevant portions of the standard. This is a perfectly valid use of pointers to local variables.

Where you have to be wary is if the function stores the pointer values in a structure that has a lifetime longer than its own call. Consider two functions, foo(), and bar():

int *g_ptr;

void bar (int *p) {
    g_ptr = p;
}

void foo () {
    int x = 10;
    bar(&x);
}

int main () {
    foo ();
    /* ...do something with g_ptr? */
    return 0;
}

In this case, the variable xs lifetime ends with foo() returns. However, the pointer to x has been stored in g_ptr by bar(). In this case, it was an error for foo() to pass a pointer to its local variable x to bar().

What this means is that in order to know whether or not it is valid to pass a pointer to a local variable to a function, you have to know what that function will do with it.

jxh
  • 64,506
  • 7
  • 96
  • 165
  • You saved my day. I was doing the exact same thing - i.e. saving pointer to an auto variable. It caused me so many agony since my struct was pointing to la la land after the function exited and had no idea what was happening. – kaushal Apr 01 '15 at 14:32
  • @jxh I really dont understand your answer. Can you elaborate it a bit ? – Suraj Jain Jul 26 '16 at 13:19
  • You said In this case, the variable xs lifetime ends with foo() returns. However, the pointer to x has been stored in g_ptr by bar(). In this case, it was an error for foo() to pass a pointer to its local variable x to bar()......Bu t bar() end before foo() so how is it an error ? – Suraj Jain Jul 26 '16 at 13:21
  • @kaushal Can you explain me what he said / – Suraj Jain Jul 26 '16 at 13:25
  • @SurajJain After `foo` returns, `g_ptr` points to an invalid object. – jxh Jul 26 '16 at 14:50
  • But when i compiled it in code blocks and dereference the pointer it still showed correct .Here is the code https://postimg.org/image/45f1tbcw7/ – Suraj Jain Jul 26 '16 at 15:03
  • @SurajJain You are barking up the wrong tree. Undefined behavior includes behaving like it should work. It doesn't make it correct. – jxh Jul 26 '16 at 15:18
  • @jxh Thanks... have you seen my code... so it is wrong right ..? – Suraj Jain Jul 26 '16 at 16:36
  • @SurajJain in simple terms, you can't access pointer to a variable after the scope of the variable (x) has ended. – kaushal Jul 26 '16 at 17:14
  • @SurajJain Please post a new question if you think you have questions about your program. – jxh Jul 26 '16 at 18:47
  • @jxh I dont have any problem i just want to confirm that i was lucky that my code worked and what did was not considered good practice. I cannot post a question as i am not allowed to some day back i posted question about float to integer conversion that was downvoted that is why. – Suraj Jain Jul 26 '16 at 19:16
  • @SurajJain When you are allowed to post a question again, please do so. – jxh Jul 26 '16 at 19:34
1

Those variables are allocated in the stack. As long as you do not return from the function that declared them, they remain valid.

Tiago A.
  • 1,292
  • 1
  • 15
  • 24
  • 1
    The stack is not part of the C model of computation. The word “stack” does not even appear in the C 1999 or C 2011 standards. – Eric Postpischil Jul 23 '13 at 00:14
  • are you sure there is no stack in C? These articles suggest otherwise: http://gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html, http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/stack.html, http://www.quora.com/What-is-the-stack-and-heap-memory-architecture-used-by-C – tmsimont Nov 29 '14 at 15:44
0

As I'm not yet allowed to comment, I'd rather write another answer as amendment to jxh's answer above:

Please see my elaborate answer here for a similar question. This contains a real world example where the aliasing in the called function makes your code break even though it follows all the c-language rules.

Even though it is legal in the C-language I consider it as harmful to pass pointers to automatic variables in a function call. You never know (and often you don't want to know) what exactly the called function does with the passed values. When the called function establishes an alias, you get in big trouble.

Community
  • 1
  • 1
opt12
  • 300
  • 3
  • 10