In C there are three kinds of pointer values:
- Values that are NULL, because the programmer initialized them (or because they took advantage of default static initialization).
- Values that were returned by a pointer-returning function such as
fopen
or malloc
(and that have not yet been passed to fclose
or free
).
- Values where neither 1 nor 2 is true.
And the simple fact is that if you have a pointer of kind 3, there is no mechanism in the language that will tell you whether the pointer is valid or not. If you have a pointer p
that might have been obtained from malloc
or not, but you can't remember, there is no way to ask the compiler or run-time system to tell you if it currently points to valid memory. If you have a FILE pointer fp
that might have been obtained from fopen
or not, but you can't remember, there is no way to ask the compiler or run-time system to tell you if it currently "points to" a valid file.
So it's up to you, the programmer, to keep track of pointer values, and to use programming practices which help you determine whether pointer values are valid or not.
Those ways include the following:
- Always initialize pointer variables, either to NULL, or to point to something valid.
- When you call a function that returns a pointer, such as
fopen
or malloc
, always test the return value to see if it's NULL, and if it is, return early or print an error message or whatever is appropriate.
- When you're finished with a dynamically-allocated pointer, and you release it by calling
fclose
or free
or the equivalent, always set it back to NULL.
If you do these three things religiously, then you can test to see if a pointer is valid by doing
if(p != NULL)
or
if(p)
Similarly, and again if you do those things religiously, you can test to see if a pointer is invalid by doing
if(p == NULL)
or
if(!p)
But those tests work reliably only if you have performed steps 1 and 3 religiously. If you haven't, it's possible -- and quite likely -- for various pointer values to be non-NULL but invalid.
The above is one strategy. I should point out that steps 1 and 3 are not strictly necessary. The other strategy is to apply step 2 religiously, and to never keep around -- never attempt to use -- a pointer that might be null. If functions like fopen
or malloc
return NULL, you either exit the program immediately, or return immediately from whatever function you're in, typically with a failure code that tells your caller you couldn't do your job (because you couldn't open the file you needed, or you couldn't allocate the memory you needed). In a program that applies rule 2 religiously, you don't even need to test pointers for validity, because all pointer values in such programs are valid. (Well, all pointers are valid as long as Rule 2 was applied religiously. If you forget to apply Rule 2 even once, things can begin to break down.)