Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> memset (sprtemp,-1, sizeof(sprtemp));

Yikes. I think this article undersells the point somewhat. This line of code undermines the type system by spraying -1's into an array of structs, so the only surprise to me is that it took this long to break.



`spriteframe_t` is defined as

  typedef struct
  {
    // If false use 0 for any position.
    // Note: as eight entries are available,
    //  we might as well insert the same name eight times.
    boolean rotate;

    // Lump to use for view angles 0-7.
    short lump[8];

    // Flip bit (1 = flip) to use for view angles 0-7.
    byte flip[8];
    
  } spriteframe_t;
which is okay to splat with all-ones as long as `boolean` is either a typedef for a fundamental type(*) or an enum – because C enums are just ints in a trenchcoat and have no forbidden bit patterns! The C99 `bool`/`_Bool` is, AFAICS, the first type in C that has fewer values than possible bit patterns.

So yeah, on C99 and C++ this always had UB and could've broken at any time – though I presume compiler devs were not particularly eager to make it ill-behaved just because. But in pre-C99 it's entirely fine, and `rotate == true || rotate == false` could easily be false without UB.

---

(*) other than `char` for which setting the MSB is… not UB but also not the best idea in general.


And, indeed, if you look at the author's writeup, you see exactly that the generated code satisifes `(rotate == true || rotate == false) == false`, since rotate is checked explicitly against 0 and 1. The essence of the difference is:

> When boolean is an enum, the compiler does exactly what I expected – the == false condition checks if the value is equal to 0, and the == true condition checks if the value is equal to 1.

> However, when boolean is actually _Bool, the == false check is transformed into != 1, and the == true check is transformed into != 0 – which makes perfect sense in the realm of boolean logic. But it also means that for a value of 255, hilarity ensures: since 255 is neither 0 nor 1, both conditions pass!

So a value of 255 also makes both checks fail for the enum, but because of the ordering of the code, it was expected to evaluate as != false.

Had the check: ``` if (sprtemp[frame].rotate == false) ```

been written as: ``` if (sprtemp[frame].rotate != true) ```

then it would work for the `bool` type, but not the `enum` type, at least in C23 mode. Assumedly the C++ mode (effectively) treated the boolean as an enum, or possibly as `false == 0`, `true != 0`.


Maybe changing the type of the variable to int was the better solution?


C99 does not use bool unless you explicitly include stdbool.h, which won't be in any pre-C99 code. By default, you just get _Bool.


C has no particularly strong type system, and it works on typical platforms with all types up to the introduction of _Bool. And maybe float/double, but I think it gives a NaN.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: