Why this program doesn't crash, is it undefined behaviour ?
int main()
{
char* c;
if (c) {
printf("called free\n");
free (c);
}
else {
printf("not called free\n");
}
printf("not crashed\n");
c = strdup("someString");
if (c) {
printf("called free\n");
free(c);
c = NULL; // why is this needed for last if (c)
}
if (c) {
free(c);
}
printf("still not crashed\n");
return 0;
}
First, some background: there are three kinds of pointer values:
&
, or by calling malloc
or realloc
or strdup
, and as long as the pointer has not become invalid.&
) to point to local variables in functions ("stack frames") that are no longer active; pointers that were once allocated using malloc
or the others, but that have since been deallocated using free
, or reallocated using realloc
.You can tell if a pointer is in category 1 using an ordinary comparison: if(p == NULL)
, if(p != NULL)
.
But the key point is that there is no programmatic way of distinguishing between pointer values in categories 2 and 3. That is, there is no way to write if(valid(p))
or if(invalid(p))
.
So the only way to avoid using invalid pointers is to keep track, yourself, somehow, of which pointers are valid and which are not. One strategy for doing this, pretty widely recommended as a good habit, is to set pointer variables to NULL whenever you know they're invalid.
See also this answer and this answer.
Now, for some specific answers to your code and questions:
int main()
{
char* c;
So c
is a local variable. Since it is not initialized, it is unquestionably invalid (that is, category 3).
if (c) {
printf("called free\n");
free (c);
}
else {
printf("not called free\n");
}
This is a strange piece of code. It seems to acknowledge the possibility that the uninitialized pointer c
might reliably start out as a null pointer, which as I just said is not the case. Also, this code is actually arranged, if anything, to maximize the possibility of a crash. If, by chance, c
starts out as a null pointer, free
is not called — but it turns out that free
is guaranteed to behave gracefully — by quietly doing nothing — if it's passed a null pointer. Only if c
is non-null — meaning that it's guaranteed to be invalid — is it handed to free
, where it's virtually guaranteed to cause trouble.
c = NULL; // why is this needed for last if (c)
As mentioned, it's a good habit to set pointers to NULL
after freeing them. A null pointer is testably invalid, while a non-NULL but freed pointer is undetectably invalid.
- Is any possibility that this program will ever crash ?
Yes, it is quite likely to crash, since if tries to free invalid, uninitialized pointer, and (without that "why is this needed?" line), it frees a pointer twice.
If you ran this code, and it didn't crash, that's kind of interesting, but it doesn't prove anything. Code that exhibits undefined behavior, such as trying to use the value of an uninitialized or otherwise invalid pointer, is not guaranteed to work. But it is not guaranteed not to work. "Undefined behavior" means that anything can happen, including code that appears to magically work. Undefined behavior does not mean that code is guaranteed to crash, or anything like that.
- Should I always make such a
if (c)
verification before I callfree
onchar*
?
You can, but I don't think it does what you think it does.
It's not necessary, because as I mentioned, if you have a pointer that's already null, it's harmless to call free
on it. So the test, in and of itself, doesn't protect you from anything.
If you thought that the if(c)
test did protect you from something, like by making sure you don't accidentally free an invalid pointer, it's not necessarily any good for that at all — unless you're religious about always setting pointers to NULL
when you know they've become invalid.