A few days ago I was working on one of project in C using MPI. There I was doing something stupid and my program was showing a strange behaviour. In order to understand this let's dive deep into details.
Below is a simple java code
Now what would happen if I write
and run it. Obviously we will get an ArrayIndexOutOfBoundsException.
Now let's try to do same in C.
Compile and run it and let me know in comments what happened?(without proceeding to read next paragraph).
Surprisingly it did not give segmentation fault although we have got "ArrayIndexOutOfBoundsException" in case of java(and similar exception in other high level programming languages) but not in C. In order to investigate it we have to do a little but more work.
Let's try to change index 5 to 6 and rerun it. It again run without any issue. Change index to 7, 8, 9 and ultimately 10. But it still did not crash. Instead of modifying the index value every time manually and then compiling and running we can automate this. Modify our program a little bit and write a script that will automatically increment the index. Here is the modified program.
Below is the script that will run this program iteratively
First we get index from the argument passed to program and then accessing array value at that index to obtain array value and to assign a dummy variable 'x' instead of using 'printf'. Next in script we run a loop from 0 to 500 and at each iteration of loop run the program with loop index and observe its return value. If program returned normally that means no segmentation fault occurred and loop continues to run and if a segmentation fault occurs we receive a non-zero value, thus we break our loop and print the value of i where segmentation fault occurred.
For me the segmentation fault occurred at the value of 292. The reason for segmentation fault not occurring at index 5 and beyond lies in the concept of virtual memory.
Virtual memory is a paging based mechanism inside Operating System(OS) that gives a program an illusion that it holds entire memory. This enables OS to execute programs larger than the physical memory. This virtual memory is organized into number of virtual pages or simple pages. A C program will not receive "segmentation fault" signal as long as it is accessing the memory which lies within its virtual page. This is the reason we were not getting any segmentation fault even if we were accessing a memory location beyond the array size and once we crossed the page limit we got segmentaion fault.
Let me know about your thoughts in comments.
Note: I am using Apple Mac Book pro, OS X 10.11.5(EL Capitan), LLVM version 7.3.0(clang version 703.0.31) that ships with Xcode 7.3.1 for compiling/running all of the above code.
Below is a simple java code
1 2 3 4 5 | public class Main { public static void main(String[] args) { int[] array = {1, 2, 3, 4, 5}; } } |
System.out.println(array[5]);
Now let's try to do same in C.
1 2 3 4 5 6 7 8 | #include <stdio.h> int main(int argc, const char * argv[]) { int array[] = {1, 2, 3, 4, 5}; printf("%d\n", array[5]); return 0; } |
Compile and run it and let me know in comments what happened?(without proceeding to read next paragraph).
Surprisingly it did not give segmentation fault although we have got "ArrayIndexOutOfBoundsException" in case of java(and similar exception in other high level programming languages) but not in C. In order to investigate it we have to do a little but more work.
Let's try to change index 5 to 6 and rerun it. It again run without any issue. Change index to 7, 8, 9 and ultimately 10. But it still did not crash. Instead of modifying the index value every time manually and then compiling and running we can automate this. Modify our program a little bit and write a script that will automatically increment the index. Here is the modified program.
1 2 3 4 5 6 7 8 9 | #include <stdio.h> #include <stdlib.h> int main(int argc, const char * argv[]) { int array[] = {1, 2, 3, 4, 5}; int x = array[atoi(argv[1])]; return 0; } |
Below is the script that will run this program iteratively
1 2 3 4 5 6 7 8 9 10 11 | rm main clang main.c -o main for i in {0..500} do ./main $i res=$? if [ $res != 0 ];then echo segmentation fault at $i break fi done |
First we get index from the argument passed to program and then accessing array value at that index to obtain array value and to assign a dummy variable 'x' instead of using 'printf'. Next in script we run a loop from 0 to 500 and at each iteration of loop run the program with loop index and observe its return value. If program returned normally that means no segmentation fault occurred and loop continues to run and if a segmentation fault occurs we receive a non-zero value, thus we break our loop and print the value of i where segmentation fault occurred.
For me the segmentation fault occurred at the value of 292. The reason for segmentation fault not occurring at index 5 and beyond lies in the concept of virtual memory.
Virtual memory is a paging based mechanism inside Operating System(OS) that gives a program an illusion that it holds entire memory. This enables OS to execute programs larger than the physical memory. This virtual memory is organized into number of virtual pages or simple pages. A C program will not receive "segmentation fault" signal as long as it is accessing the memory which lies within its virtual page. This is the reason we were not getting any segmentation fault even if we were accessing a memory location beyond the array size and once we crossed the page limit we got segmentaion fault.
Let me know about your thoughts in comments.
Note: I am using Apple Mac Book pro, OS X 10.11.5(EL Capitan), LLVM version 7.3.0(clang version 703.0.31) that ships with Xcode 7.3.1 for compiling/running all of the above code.