Post by Janis PapanagnouPost by James KuyperPost by Janis Papanagnou[...] my test-wise - deliberately
wrong! - assignment to 'a[99]' produced also no compiler complaints,
BTW, it produced also no core dump or any other runtime error.
(But it is obviously severely wrong code anyway.)
Accessing an array beyond it's bounds has undefined behavior, whether or
not it is a VLA. "undefined behavior" means that the C standard imposes
no requirements on the behavior. In particular, it does not require a
diagnostic message, nor a core dump, nor any other kind of runtime error.
Yes. (There's reasons why I prefer programming languages with checks.)
Post by James Kuyper[...]
In this particular case. however, it would be trivial to detect the
violation, but none of the compilers I've tested do so.
Yes, because it's a special case with a constant defined. But the
value may come from "outside", undetectable by the compiler and only
detectable during runtime if there's additional information maintained
(stored and checked).
Post by James KuyperWhen such code is accepted by an implementation, what is most likely to
happen is that the compiler will generate code that creates an array
containing "foobar", and which attempts to write a pointer to the first
element of that array to the location where a[99] should be, if a had at
least 100 elements. Depending upon what that piece of memory is being
used for, the results could be catastrophic, or completely innocuous, or
somewhere in-between.
Yes. That matches exactly my fears or expectations here [with "C"].
Janis
Let's assume your full code is :
int main() {
int n = 5;
char * arr[n];
arr[99] = "foobar";
}
In C, that means exactly the same as a do-nothing program:
int main() { }
There is no observable behaviour. So an optimising compiler (like gcc,
if you have enabled optimisation) will give a program that simply returns 0.
If optimisation is not enabled, or a weak compiler is used, the array
will be put on the stack and your assignment will write to a part of the
stack that exists in memory. It /might/ stomp over something important,
but there's a fair chance that - by luck - nothing bad happens.
The C language does not have much in the way of checks other than for
syntax, grammar and constraints. But C /implementations/ do far better,
as long as you enable them.
When you are checking code like this, I recommend you get in the habit
of using <https://godbolt.org> for convenience, as it makes it easy to
check snippets with the newest compiler versions. And use command-line
flags such as :
-std=c99 -Wpedantic -Wall -Wextra -O2
The "-Wpedantic" makes sure that all standards-required diagnostics are
produced (to the best of the compiler's abilities). "-Wall" should
always be used as a basis to give useful warnings. "-Wextra" is more
debatable - it warns on things that some people feel is good coding
practice, other people feel is poor coding practice. But I think it is
very useful for this kind of thing, even if you don't use it in your
main development. And it is a good idea to enable optimisation - it
makes warnings better, and it gives you a better idea of what your code
/really/ means.
So with this example code, you'd see the virtually empty generated
assembly, and a warning that "arr" is set but never used. (I am
disappointed that neither gcc nor clang can spot the clearly
out-of-bounds access.)
Sometimes gcc will give the best static error checking, sometimes clang
- use both tools.
The next step is to use run-time checkers. Compile the code with the
flag "-fsanitize=undefined" and you will get a run-time check and
run-time error on the array bounds violation. (You can do this from
within godbolt.org).
C (and C++) have a vast range of checks available to developers - you
just have to know how to use them.