views:

332

answers:

8

Does the following code segault at array[10] = 22 or array[9999] = 22?
I'm just trying to figure out if the whole code would execute before it seg faults. (in the C language).

#include <stdio.h>
int main(){

int array[10];
int i;
for(i=0; i<9999; ++i){
    array[i] = 22;
}    
return 0;    
}
+12  A: 

It depends... If the memory after array[9] is clean then nothing might happen, until ofcourse one reaches a segment of memory which is occupied.

Try out the code and add:

 printf("%d\n",i);

in the loop and you will see when it crashes and burns. I get various results, ranging from 596 to 2380.

Daniel Persson
Yes, you might get lucky. To really understand what is going on, you should step through the disassembly in a debugger and watch for the segfault. It will be a worthwhile exercise.
jeffamaphone
It doesnt print anything, it just segfaults which suggests floatsink is probably right and your answer is wrong?
This also depends on the memory alignment and on various things like the space the compiler allocated for your variables. Perhaps you get lower results if you first allocate some space (around 1 MB) for other things.
schnaader
Another recommendation for debugging is the excellent tool valgrind at: http://valgrind.org/it will emulate an execution enviroment and can catch overflows and other more or less common mistakes.
Daniel Persson
learning2code, on what machine are you running the code and which compiler are you using?
Daniel Persson
While the answer is absolutely correct, using printf's for debugging is a bad practice :)
NicDumZ
floatsink is definitely wrong. C is not Java. C does not perform any array bounds checking. What happens is that you eventually walk outside the memory allocated to your process and *then* take a segfault.
Eddie
Using the original version (int array[10]), I also get big results around 5000, but using "int* array = new int[10];", it crashes on low values around 14, so it's also depending on stack/heap.
schnaader
No, Daniel Persson's answer is "more correct" than floatsinks, becasue your program might be able to access array[400] even though you didn't allocate it yourself. As schnaader suggested, the compiler might leave some space in there, or the OS over allocated the requirement for the buffer etc. Either way, you could legally be allowed to access 400 bytes more than you expect and you might not seg fault at all.As for it not printing anything, did you definately do this: for(i=0; i<9999; ++i){ printf("%d\n",i); array[i] = 22; }
Pod
print to stderr because stdout is buffered and you are likely to see no output at all and buffers are not flushed on signal.
qrdl
SIGSEGV is the result of touching pages not mapped to your process's address space. In the case of stack overruns, it depends on how big the stack is and which way it grows. For heap allocations, what your process gets is always a multiple of a page size. One page can be broken up into many allocations. The SIGSEGV doesn't trigger until you walk off the end of a page and the next virtual address isn't mapped, and can't be faulted in. See my answer for more.NicDumZ, there's nothing wrong with using printf() for debugging. In some cases, it can even be one of the better choices.
Steve Madsen
A: 

I suggest using GDB to investigate such problems: http://www.gnu.org/software/gdb/documentation/

mateusza
A better idea is to just not go outside allocated memory in the first place. ;)
jalf
+9  A: 

Use a debugger?

$ gcc -g seg.c -o so_segfault
$ gdb so_segfault
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) run
Starting program: /.../so_segfault 
Program received signal SIGSEGV, Segmentation fault.
0x080483b1 in main () at seg.c:7
7       array[i] = 22;
(gdb) print i
$1 = 2406
(gdb)

In fact if you run this again, you will see that the segfault will not always occur for the same value of i. What is sure is that it happens when i>=10, but there is no way to determine the value for i for which it will crash, because this is not deterministic: it depends on how the memory is allocated. If the memory is free until array[222] (aka no other programs use it), it will go on until i=222, but it might as well crash for any other value of i>=10.

NicDumZ
edited to remove breakpoint + continue; using gcc's -o option for clarity.
NicDumZ
Strictly speaking, it can happen anytime i >= 10.
Steve Madsen
@Steve: corrected, thanks!
NicDumZ
really strictly speaking, it can happen also for i<10, since the standard makes no requirement on the implementation for programs doing UB, even for code execution *before* the UB happens :) So since we know the loop would go on after i=9, the loop could crash already at i=0 :)
Johannes Schaub - litb
+1  A: 

Where your code will segfault depends on what compiler you're using, luck, and other linking details of the program in question. You will most likely not segfault for i == 10. Even though that is outside your array, you will almost certainly still have memory allocated to your process at that location. As you keep going beyond your array bounds, however, you will eventually leave the memory allocated to your process and then take a segfault.

However, if you write beyond an array boundary, you will likely overwrite other automatic variables in your same stack frame. If any of these variables are pointers (or array indexes used later), then when you reference these now-corrupted values, you'll possibly take a segfault. (Depending on the exact value corrupted to and whether or not you're now going to reference memory that is not allocated to your process.)

This is not terribly deterministic.

Eddie
A: 

More generally, you can figure out where the segmentation fault occurs in Linux systems by using a debugger. For example, to use gdb, compile your program with debugging symbols by using the -g flag, e.g.:

gcc -g segfault.c -o segfault

Then, invoke gdb with your program and any arguments using the --args flag, .g.:

gdb --args ./segault myarg1 myarg2 ...

Then, when the debugger starts up, type run, and your program should run until it receives SIGSEGV, and should tell you where it was in the source code when it received that signal.

Matt J
Though as was pointed out in another answer, if the SIGSEGV is a result of accessing a clobbered pointed, this will tell you where you dereferenced the pointer, not where it was clobbered. It won't help greatly in tracking down the bug.
Steve Madsen
+2  A: 

When and if your code crashes is not deterministic. It'll depend on what platform you're running the code on.

array is a stack variable, so your compiler is going to reserve 10 * sizeof(int) bytes on the stack for it. Depending on how the compiler arranges other local variables and which way your stack grows, i may come right after array. If you follow Daniel's suggestion and put the printf statement in, you may notice an interesting effect. On my platform, when i = 10, array[10] = 22 clobbers i and the next assignment is to array[23].

A segmentation violation occurs when user code tries to touch a page that it does not have access to. In this case, you'll get one if your stack is small enough that 9999 iterations runs out off the stack.

If you had allocated array on the heap instead (by using malloc()), then you'll get a SIGSEGV when you run off the end of a page boundary. Even a 10 byte allocation will return a whole page. Page sizes vary by platform. Note that some malloc debuggers can try to flag an array out-of-bounds case, but you won't get the SIGSEGV unless the hardware gets involved when you run off the end of the page.

Steve Madsen
+1  A: 

segmentation fault happens when accessing outside of the processes dedicated memory, this is not easily predicted. When i == 10 it's outside the array.. but might still be in the memory of the process. This depends how the processes memory got allocated, something there is no way (normally) of knowing (depending of the memory manager of the OS). So segfault might happen at any of i = 10 - 9999, or not at all.

Joakim Elofsson
+5  A: 

The answer is maybe. The C language says nothing about what should happen in this case. It is undefined behavior. The compiler is not required to detect the problem, do anything to handle the problem, terminate the program or anything else. And so it does nothing.

You write to memory that's not yours, and in practice one of three things may happen:

  • You might be lucky, and just get a segfault. This happens if you hit an address that is not allocated to your process. The OS will detect this, and throw an error at you.
  • You might hit memory that's genuinely unused, in which case no error will occur right away. But if the memory is allocated and used later, it will overwrite your data, and if you expect it to still be there by then, you'll get some nice delayed-action errors.
  • You might hit data that's actually used for something else already. You overwrite that, and sometime soon, when the original data is needed, it'll read your data instead, and unpredictable errors will ensue.

Writing out of bounds: Just don't do it. The C language won't do anything to tell you when it happens, so you have to keep an eye on it yourself.

jalf
+1 for saying you are LUCKY when you get a segfault. Errors can be VERY hard to find if you access outside an array and don't get a segfault.
Dolphin