Hacker News new | comments | ask | show | jobs | submit login

Local variables in C and C++ are just put on a stack (i.e. the stack) and its behavior is pretty predictable. For example:

  #include <stdio.h>
  
  static int *foo(void)
  {
      int i = 42;
      return &i;
  }
  
  static int *bar(void)
  {
      int i = 43;
      return &i;
  }
  
  int main(void)
  {
      int *f = foo();
      /* with next line commented out prints 42, with it, 43 */
      bar();
      int i = *f;
      printf("%i\n", i);
      return 0;
  }
Until you've made another function call that reuses that space on the stack you'll almost certainly still have valid values when accessing those local variables by address. Things are a bit vaguer in C++ where those variables may be objects which have destructors which have already been called.

Edit: Made it predictable, per caf's (correct) comment by adding:

  int i = *f;



Also compiling with heavy optimization (such as -O3) can blow a variable out of the stack and into a register that can quickly get reused, or can also inline functions. Both of this can break the perceived predictability.

In fact, here on my computer, your code example behaves differently when compiled with gcc default optimizations and when using -O3.

  [peter@laptop ~]$ gcc test.c -o test
  [...] warnings
  [peter@laptop ~]$ ./test
  43
  [peter@laptop ~]$ gcc -O3 test.c -o test
  [...] warnings
  [peter@laptop ~]$ ./test
  42
I'm inclined to believe in this case this is happening due to function inlining, but I haven't checked the assembly.

Edit: My post refers to the last version of your code, with the int i = *f; line in it.


It's not entirely predictable, because, for example, the compiler may have already stored the first parameter of printf (the address of "%i\n") before it gets around to dereferencing f, and it may have stored it in the same location on the stack that was used for `i` in foo and bar.

It's also possible that foo() got inlined, so that the space used for `i` in foo() ends up being used for `f` in main.


In embedded systems where interrupts are handled from user space (or the code you show is in the kernel), then it is guaranteed that your stack will be obliterated if an interrupt occurs at the exact right time, so your value will not be there.

This is not academic: I have debugged code that relied on that behavior, and would fail once in a million runs. Very frustrating to figure out.


Same in userland if you're using POSIX signals (and you haven't explicitly configured a separate sigstack)

All it takes is the user resizing their xterm and JUST the right moment and the SIGWINCH handler will happily step on your out-of-scope stack objects.


Very true.

Of course, the exact details depend on the architecture. For example, I've been doing a bunch of PIC24 microcontroller programming recently, in both C and assembly. On these, an interrupt will store the return address on the stack, obliterating the variable. However, if more dead variables are on the stack, they may be left intact, since PIC24 uses shadow-registers to stash away the users register values temporarily so that the interrupt may reuse them. That is, they are not temporarily pushed onto the stack (obliterating more dead variables), but rather stashed in shadow registers.

Actually, interrupts store two 16bit values from the stack - a 24bit instruction pointer and the lower 8bit of the STATUS register, so would destroy 32 bits worth of dead variables' values.


Standarians will point out that what you've said is implementation dependent, for the standard makes no claims as to what you are talking about other than defining the lifetime of objects. Your edit does not make it predictable beyond the specific implementation you tested it on.


It's not that predictable - try changing bar() to

  static int *bar(int dummy)
and call it with

  bar(10);




Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: