I thought it would be interesting to see how some different compilers actually behave here. This was my test program:
nate@haswell:~/tmp$ cat deref.c
/* gcc -fno-inline -O3 -g -Wall deref.c -o deref */
/* (or as desired to show particular outcome) */
#include <stdio.h>
#include <stdlib.h>
void deref_and_print(int *ptr) {
int i = *ptr; /* undefined behavior if ptr is NULL */
if (ptr == NULL) printf("NULL\n");
else printf("%d\n", i);
}
int main(int argc, char **argv) {
int *ptr = NULL;
if (argc <= 1) {
printf("Usage: %s [arg]\n", argv[0]);
printf(" Demonstrates undefined behavior if 'arg'");
printf(" is not a positive integer\n");
exit(1);
}
int i = atoi(argv[1]);
if (i > 0) ptr = &i;
deref_and_print(ptr);
exit(0);
}
As you guessed, icc, gcc, and clang all crash with a segfault for 'deref 0' when compiled with -O0. But more interestingly, when compiled with -O1, -O2, or -O3, clang prints "NULL", while icc and gcc continue to crash.Nice test. Surprising results for clang. I take it that it doesn't crash for you when it prints NULL? That's the case for me. I'd have thought it would either remove the NULL check and crash, or leave it in but continue to dereference NULL, and thus crash.
The code when it doesn't crash has been reorganized by the compiler so the indirection doesn't even happen until it is preparing the arguments for the printf, which is in an else after the NULL check. If you don't need the value of a computation in one of two sides of an if statement and you know there are no legitimate side effects (I say "legitimate", as crashing does not count) to the calculation, it makes sense to delay the execution of that logic until and unless you actually need it done. I thereby don't find either result surprising here: it is just a demonstration of different optimization decisions and abilities of the two compilers.
I'm pretty sure the dereference is immediately UB (other than the case you quote here). Consider that when optimizations are off, that line will probably immediately execute a load instruction, generating a segfault.