This is a little bit hard to explain, but, in essence, in systems where a fraction of memory is mapped to a process (see paging and segmentation in protected mode for Intel processors as examples), the processor implements an interrupt routine which deals if "faults". Again, as an example using Intel x86 family processors, the first 32 interruptions are reserved to the processor use. The 14th interruption (interrupt 13) is called GENERAL PROTECTION FAULT (GPF), that happens if you are trying to access memory not mapped to your process (a segment has a base address and limit... and there is paging as well)... When an address is used to access memory that it is not mapped to your process, the GPF interruption occurs, giving a chance to the kernel to do something about it.
On Unix systems a GPF will generate a signal, sent to the process, called SIGSEGV, where the default behavior (if not handled by the process) is to abort. I'm not sure because I don't deal with Windows for a long time, but I believe an "Access Violation" aborts the Windows process as well (no signal sent!).
There are other "fault" interrupts like "Page Fault" (trying to access a "page" of memory not present in physical memory), and there are "traps" (like "Breakpoint", ) and aborts (like "Double Fault" and "Machine Check" ) where there's no option unless to abort the source of the interrupt). See Intel SDM (Software Developemt Manuals) for more details.
In short, segmentation fault aren't checked by YOUR code, but by the operation system. Your code can choose how to respond to this (in the example of SIGSEGV handler on Unix systems), but usually isn't a good idea since the fault leaves your program in an unstable state. In fact, signals hardware based as SIGSEGV, SIGBUS and SIGILL can abort the process whatever you do or never abort. See this:
Code:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
static void segv_handler( int sig )
{
static const char msg[] = "Ouch!\n";
// Using write() because it is Assynchronous Signal "safe" (routines from stdio aren't!).
write( STDOUT_FILENO, msg, sizeof msg - 1 );
}
int main( void )
{
struct sigaction sa = { .sa_handler = segv_handler, .sa_flags = SA_INTERRUPT };
char *p = NULL;
sigaction( SIGSEGV, &sa, NULL );
// this will generates a GPF, since 0 isn't a valid address on
// process address space. GPF will be dealt by kernel and SIGSEGV
// is sent to the process (the default handler is to print "Segmentation fault"
// and abort).
*p = 0;
puts( "Ok!" );
}
In some systems "Ok" is never printed and "Ouch!" is printed once and the process aborted. In other systems "Ouch!" is printed ad infinitum. Maybe there are systems where "Ouch!" and "Ok!" are printed once and the program closes graciously (I've never seen one!). The usual end of SIGSEGV handler should be an exit() call, if you handle it. Add an exit( 1 ); at the end of segv_handler() and see "Ouch!" printed, instead of "segmentation fault".