Keith said:
Certainly if I write something simple like:
void func(void) {
int local = 42;
return &local;
}
then I've made a beginner's mistake, and it's probably my own fault,
even though the compiler isn't required to diagnose it.
But I think you're ignoring the fact that many errors are *subtle*. Toy
examples, don't quite illustrate the point, but more realistic examples
are difficult to read.
Bugs are inevitable even in code written by highly skilled and
experienced programmers. Some languages do a better job than others of
helping to diagnose those errors. (C isn't one of those languages, but
there are valid reasons for the tradeoffs C makes.)
Yes, that is true; many errors are very subtle, and they are extremely hard
to pinpoint, even when employing specialized tools to weed them out.
Nonetheless, there is nothing subtle about assigning a global pointer to the
reference of a local object, and then expecting to access it beyond the
object's lifetime. A programmer must expressly write code for this effect,
and a programmer only does this if he doesn't know what he is doing.
Regarding how some languages do a better job than others of helping to
diagnose those errors, this is debatable. In this case, the C standard
leaves this behavior undefined to open the door for compiler developers to
implement any memory management policy they see fit. For this purpose, it
wouldn't be reasonable to mandate strict rules that interpreted this form of
memory access as being an error, as it would negate the motivation behind
leaving this behavior undefined. Yet, just because the standard doesn't
enforce any sanity check in this circumstance, this doesn't mean compiler
developers are barred from implementing them. In fact, some compilers do
test for this sort of error. For example, consider the following code:
<code>
int* test(void)
{
int test = 1;
return &test;
}
int main(void)
{
int *foo = test();
return 0;
}
</code>
When compiling this code with gcc, and without passing any flags, the
following warning is shown:
<message>
rui@Kubuntu:tmp$ gcc main.c
main.c: In function ‘test’:
main.c:5:2: warning: function returns address of local variable
</message>
When compiling this code with clang, the following warning is shown:
<message>
rui@Kubuntu:tmp$ clang main.c
main.c:5:10: warning: address of stack memory associated with local variable
'test' returned
return &test;
~^~~~
1 warning generated.
</message>
This diagnostic isn't mandated by the C standard, but at least two compilers
have implemented it. This doesn't mean that the C programming language
suddenly became safer with these platform-specific diagnostics, or that it
was less safe in the past. The language is still the same, the code is
still broken and the sole cause is still the programmer's incompetence.
By explicitly defining the limits where a specific behaviour must be
expected then the C standard clearly defines what represents valid C
code, which every competent C programmer is expected to understand.
[...]
Understanding the limits is one thing. Consistently avoiding them while
writing or maintaining complex software is quite another.
Some limits are pretty obvious and pretty hard to miss, unless a programmer
is incompetent. This example would fit into this category.
It's also important to mention that this doesn't mean that C is free from
any safety issue. Yet, some of these accusations are baseless and absurd,
and only contribute to grow a myth about a problem which is far greater, far
more widespread and far more serious than what it really is, and this while
absolving any incompetence which caused it, no matter how profound it might
have been. This safety myth has grown so much that nowadays, by the
comments some people make, it almost sounds like even the smallest hello
world program written in C represents a potential security breach which no
other programming language is capable of causing. And this doesn't benefit
anyone.
Rui Maciel