From: antispam@fricas.org
highcrew wrote:
> Hello,
>
> While I consider myself reasonably good as C programmer, I still
> have difficulties in understanding undefined behavior.
> I wonder if anyone in this NG could help me.
>
> Let's take an example. There's plenty here:
> https://en.cppreference.com/w/c/language/behavior.html
> So let's focus on https://godbolt.org/z/48bn19Tsb
>
> For the lazy, I report it here:
>
> int table[4] = {0};
> int exists_in_table(int v)
> {
> // return true in one of the first 4 iterations
> // or UB due to out-of-bounds access
> for (int i = 0; i <= 4; i++) {
> if (table[i] == v) return 1;
> }
> return 0;
> }
>
> This is compiled (with no warning whatsoever) into:
>
> exists_in_table:
> mov eax, 1
> ret
> table:
> .zero 16
>
>
> Well, this is *obviously* wrong. And sure, so is the original code,
> but I find it hard to think that the compiler isn't able to notice it,
> given that it is even "exploiting" it to produce very efficient code.
>
> I understand the formalism: the resulting assembly is formally
> "correct", in that UB implies that anything can happen.
> Yet I can't think of any situation where the resulting assembly
> could be considered sensible. The compiled function will
> basically return 1 for any input, and the final program will be
> buggy.
You do not get the formalism: compiler applies a lot transformations
which are supposed to be correct for programs obeying the C rules.
However, compiler does not understand the program. It may notice
details that you missed, but it act essentialy blindly on
information it has. And most transformations have only limited
info (storing all things that compiler infers would take a lot
of memory and searching all info would take a lot of time).
Code that you see is a result of many transformations, possibly
hundreds or more. The result is a conseqence of all steps,
but it could be hard to isolate a single "silly" step.
> Wouldn't it be more sensible to have a compilation error, or
> at least a warning? The compiler will be happy even with -Wall -Wextra
> -Werror.
This case looks reasonably easy: when compiling 'exists_in_table'
the compiler had declaration of 'table' and knows it size is 4.
Compiler generated its output probably after noticing that
the loop would produce out of bound reference. So with some
extra effort it should be possible to generate a diagnostic.
But in general, instead of array you may have a pointer without
bound information. Or upper bound may be variable. As James
wrote, for such reasons C standard does not require a diagnostic.
Also, in the past gcc and clang did not generate diagnostics
in such situation. gcc is very complex beast and adding
diagnostics now may require nontrivial effort.
BTW: I expect that eventually gcc will warn. Ideologicaly,
using various string functions can overflow buffers in
similar ways. In the past such buffers overflow just generated
some (possibly "working") code. Now most such uses report
warnings. In fact, this problem looks like an outlier.
> There's plenty of documentation, articles and presentations that
> explain how this can make very efficient code... but nothing
> will answer this question: do I really want to be efficiently
> wrong?
By using C you implicitely gave "yes" as an answer.
> I mean, yes I would find the problem, thanks to my 100% coverage
> unit testing, but couldn't the compiler give me a hint?
Since it gave no hint it probably could not. In cases when it
can it warns (at least when you activate warnings).
> Could someone drive me into this reasoning? I know there is a lot of
> thinking behind it, yet everything seems to me very incorrect!
> I'm in deep cognitive dissonance here! :) Help!
>
--
Waldek Hebisch
--- SoupGate-Win32 v1.05
* Origin: you cannot sedate... all the things you hate (1:229/2)
|