tags:

views:

125

answers:

8

I'm trying to understand how pointers to statically allocated objects work and where they can go wrong.

I wrote this code:

int* pinf = NULL;
for (int i = 0; i<1;i++) {
    int inf = 4;
    pinf = &inf;
}

cout<<"inf"<< (*pinf)<<endl;

I was surprised that it worked becasue I thought that inf would dissapear when the program left the block and the pointer would point to something that no longer exists. I expected a segmentation fault when trying to access pinf. At what stage in the program would inf die?

+2  A: 

It probably will never die because pinf will point to something on the stack.

Stacks don't often shrink.

Modify it and you'll pretty much be guaranteed an overwrite though.

Richard Harrison
+1  A: 

You won't necessarily get a SIGSEGV (segmentation fault). inf memory is probably allocated in the stack. And the stack memory region is probably still allocated to your process at that point, so, that's probably why you are not getting a seg fault.

Pablo Santa Cruz
Especially not if you run on Windows, where you'd get an access violation rather than a segmentation fault.
DeadMG
@DeadMG: hehehehe... True.
Pablo Santa Cruz
A: 

A protection fault occurs when the memory page you point to is not valid anymore for the process.

Luckily most OS's don't create a separate page for each integer's worth of stack space.

vlabrecque
Or unluckily, since it lets bugs continue silently on their way and appear much later with horrible consequences. There's Electric Fence (efence) which will force heap allocations into their own block and help catch these errors, but I don't think there's any such solution (or indeed, ever could be one) for stack variables.
Ben Voigt
+1  A: 

The behaviour is undefined, but in practice, "destructing" an int is a noop, so most compilers will leave the number alone on the stack until something else comes along to reuse that particular slot.

Some compilers might set the int to 0xDEADBEEF (or some such garbage) when it goes out of scope in debug mode, but that won't make the cout << ... fail; it will simply print the nonsensical value.

Marcelo Cantos
+5  A: 

Your understanding is correct. inf disappears when you leave the scope of the loop, and so accessing *pinf yields undefined behavior. Undefined behavior means the compiler and/or program can do anything, which may be to crash, or in this case may be to simply chug along.

This is because inf is on the stack. Even when it is out of scope pinf still points to a useable memory location on the stack. As far as the runtime is concerned the stack address is fine, and the compiler doesn't bother to insert code to verify that you're not accessing locations beyond the end of the stack. That would be prohibitively expensive in a language designed for speed.

For this reason you must be very careful to avoid undefined behavior. C and C++ are not nice the way Java or C# are where illegal operations pretty much always generate an immediate exception and crash your program. You the programmer have to be vigilant because the compiler will miss all kinds of elementary mistakes you make.

John Kugelman
+1 for the (technically correct) information :)
Prasoon Saurav
well, the address remains valid, so it surely won't crash, and this is even worse.
ruslik
+2  A: 

You use so called Dangling pointer. It will result in undefined behavior by the C++ Standard.

Kirill V. Lyadvinsky
+2  A: 

If you are asking about this:

int main() {
  int* pinf = NULL;
  for (int i = 0; i<1;i++){
    int inf = 4;
    pinf = &inf;
  }
  cout<<"inf"<< (*pinf)<<endl;
}

Then what you have is undefined behaviour. The automatically allocated (not not static) object inf has gone out of scope and notionally been destroyed when you access it via the pointer. In this case, anything might happen, including it appearing to "work".

anon
+1 for writing Undefined Behaviour.
Prasoon Saurav
+1  A: 

The memory may or may not still contain a 4 when it gets to your cout line. It might contain a 4 strictly by accident. :)

First things first: your operating system can only detect memory access gone astray on page boundaries. So, if you're off by 4k or 8k or 16k or more. (Check /proc/self/maps on a Linux system some day to see the memory layout of a process; any addresses in the listed ranges are allowed, any outside the listed ranges aren't allowed. Every modern OS on protected-memory CPUs will support a similar mechanism, so it'll be instructive even if you're just not that interested in Linux. I just know it is easy on Linux.) So, the OS can't help you when your data is so small.

Also, your int inf = 4; might very well be stashed in the .rodata, .data or .text segments of your program. Static variables may be stuffed into any of these sections (I have no idea how the compiler/linker decides; I consider it magic) and they will therefore be valid throughout the entire duration of the program. Check size /bin/sh next time you are on a Unix system for an idea how much data gets put into which sections. (And check out readelf(1) for way too much information. objdump(1) if you're on older systems.)

If you change inf = 4 to inf = i, then the storage will be allocated on the stack, and you stand a much better chance of having it get overwritten quickly.

sarnold