If your program doesn't explicitly choose to do anything with a signal, there's a "default disposition" for each signal, either to kill the process (segmentation faults, Ctrl-C, etc.), do nothing (window resized, child process exited), or suspend the process (Ctrl-Z). (Or unsuspend it, in the case of SIGCONT, but SIGCONT is weird.) If you do want to do something with a signal, you can specify a signal handler, which runs asynchronously when a signal is received: in the middle of some code, your program randomly jumps to that handler to deal with the problem. This is the only way to deal with certain conditions like segmentation faults, since there's no way for the program to keep running. But since it could be in the middle of some code, writing signal handlers is extremely tricky because you don't know what the state of the process is. For instance, malloc() usually takes a per-thread lock on the heap: if you were signalled in the middle of an allocation, calling malloc() will deadlock. There are a few other methods of dealing with signals, like sigwaitinfo() or signalfd / kqueue, that don't have this limitation but also can't respond to things like segmentation faults.
If you don't have a signal handler, and the default disposition of the signal is to terminate your process, your exit code will contain the signal that killed you. Your parent process can look at this (usually, it's paying attention for the SIGCHLD signal) and print out a message. That's where the "Segmentation fault" message in the shell comes from: it's after the process is already dead, and the shell, the parent process, is looking at whether it exited cleanly or not.
By the way, note that if a process hasn't overridden the default signal dispositions, there's no difference between terminating it with Ctrl-C (SIGINT), Ctrl-\ (SIGQUIT), kill (SIGTERM), or kill -9 (SIGKILL). It's just that it's impossible to change the handling of SIGKILL, so that's your option if the process has a termination handler that's not working right. But otherwise, all four of them cause the process to immediately exit—and make the kernel do the same cleanup. (It is this cleanup that can get stuck and cause a process to be unresponsive even to kill -9: it means the process is already as dead as it can be, but the kernel is trying to close file descriptors or unmap memory and got stuck doing that.)