1. 24
  1. 8

    An extreme example of unportable C is the book Mastering C Pointers: Tools for Programming Power, which was castigated recently. To be fair, that book has other flaws rather than being in a different camp, but I think that fuels some of the intensity of passion against it.

    This… rather grossly undersells how much is wrong with that book. The author didn’t understand scope, for crying out loud, and never had a grasp of how C organized memory, even in the high-level handwavy “C Abstract Machine” sense the standard is written to.

    There are better examples of unportable C, such as pretty much any non-trivial C program written for MS-DOS, especially the ones which did things like manually writing to video memory to get the best graphics performance. Of course, pretty much all embedded C would fit here as well, but you’ll actually be able to get and read the source of some of those MS-DOS programs.

    In so doing, the committee had to converge on a computational model that would somehow encompass all targets. This turned out to be quite difficult, because there were a lot of targets out there that would be considered strange and exotic; arithmetic is not even guaranteed to be twos complement (the alternative is ones complement), word sizes might not be a power of 2, and other things.

    Another example would be saturation semantics for overflow, as opposed to wraparound. DSPs use saturation semantics, so going off the top end of the scale plateaus, instead of causing a weird jagged waveform.

    As for the rest, it’s a hard problem. Selectively turning off optimization for specific functions would be useful for some codebases, but aggressive optimization isn’t the only problem here: Optimization doesn’t cause your long type to suddenly be the wrong size to hold a pointer on some machines but not others. Annotating the code with machine-checked assumptions about type size, overflow behavior, and maybe other things would allow intelligent warnings about stupid code, but… well… try to get anyone to do it.

    1. 7

      Re “Mastering C Pointers,” that’s fair. I included it because it’s one of the things that got me thinking about the unportable camp, but I can see how its (agreed, very serious) flaws might detract from the overall argument I’m making and that there might be a better example.

      Re saturating arithmetic, well, Rust has it :)

      1. 2

        My interpretation is that the point of C is that simple C code should lead to simple assembly code. Needing to write SaturatedArithmetic::addWithSaturation(a, b) instead of just a + b in all arithmetic DSP code would be quite annoying, and would simply lead to people using another language.

        You could say ‘oh they should add operator overloading’, but then that contravenes the first point, that simple C code (like a + b) should not hide complex behaviour. The only construct in C that can hide complexity is the function call, which everyone recognises. But if you see some arithmetic, you know it’s just arithmetic.

        1. 1

          You could say ‘oh they should add operator overloading’, but then that contravenes the first point, that simple C code (like a + b) should not hide complex behavior. The only construct in C that can hide complexity is the function call, which everyone recognizes. But if you see some arithmetic, you know it’s just arithmetic.

          Not to mention that not everything can be overloaded, causing inconsistencies, and some operations in mathematics have operators other than just “+-/*”. Vector dot product “·”, for example. Even if CPP (or any other language) extends to support more operators, these operators can’t be reached without key composition (“shortcuts”), making it almost unwanted. vec_dot() might require typing more, but it’s reachable to everyone, and operators don’t need to have hidden meanings.

          1. 2

            Eh, perl6 seems to do just fine with 60 bajillion operators.

            1. 2

              Perl does have more operators than C, but all of them are operators that can be typed using simple key composition, such as [SHIFT+something]. String concatenation for example.

              My point, added with what @milesrout said, is that some operators (math operators) aren’t easy to type with just [SHIFT+something]. As result, operator overloading in languages that offer operator overloading will always stay in a unfinished state, because it will only compromise those operators that are easily composed.

      2. 1

        Mastering C Pointers: Tools for Programming Power has several four-star reviews on Amazon uk.

        Herbert Schildt’s C: The Complete Reference is often touted as the worst C book ever and here.

        Perhaps Mastering C Pointers is the worst in its niche (i.e., pointers) and Schildt’s is a more general worst?

        1. 2

          Mastering C Pointers: Tools for Programming Power has several four-star reviews on Amazon uk.

          So? One of the dangers of picking the wrong textbook is thinking it’s great, and using it to evaluate subsequent works in the field, without knowing it’s shit. Per hypothesis, if it’s your first book, you don’t know enough to question it, and if you think it’s teaching you things, those are the things you’ll go on to know, even if they’re the wrong things. It’s a very pernicious bootstrap problem.

          In this case, the book is objectively terrible. Other books being bad don’t make it better.

          I do agree that Schildt’s book is also terrible.

      3. 4

        …or we can finally stop using C. ;)

        1. 5

          I touched on this. It’s not practical at all, not because we have to write programs in C, but because we have to use C ABI as a glue layer between modules of any modern system. It’s interesting to explore whether it would be possible to design something better, but actually getting it adopted would be an extreme uphill battle.

          1. 3

            In a recent project, where I needed Python interface with an OCaml library, I quickly assessed performance requirements and ended up passing data serialized in JSON. Maybe I’ll change it to a faster or more type safe serialization method in the future. Algebraic types can be encoded in Protobuf relatively nicely.

            It does feel like a dirty hack at one level, but if none of those have to actually interface with logic written in C, I really don’t feel like downgrading the data structures to the point where C would undersand what it passed between them.

            1. 1

              but because we have to use C ABI as a glue layer between modules of any modern system

              In that case, you write in something better that:

              1. Uses C’s data types and calling conventions.

              2. Seemlessly imports C libraries with zero-overhead calls due to No 1.

              3. Optionally outputs C code for use with arbitrary compilers.

              You get the benefits of the better language while plugging into and using the C ecosystem. Anyone wanting a language project should consider converting a popular one that’s a contender for C replacement in some scenarios to use 1-3 if it doesn’t already. Both re-implementing the interpreter/compiler/JIT and some automated tool to convert the library where the data types matter. The fork might even take off when the performance benefits kick in.

            2. 2

              Oh, I agree… but the problem of Undefined Behaviour will not go away.

              My favourite example is….

              Suppose you write a routine to calculate the square root.

              Now you break it up into two parts, and outer “safe” variant, that checks for negative arguments and raises an exception or returns an error code or something.

              And an inner routine that assumes the argument is positive and just goes ahead and calculates the sqrt of a positive float.

              Now your cow-orker (or yourself on a bad day) comes across this and says, Nah, my values are always positive, I’ll optimize a bit and just call the inner routine directly.

              Except when they aren’t.

              Weeeeeehah! Undefined Behaviour is back! And it doesn’t matter which language you chose!

              1. 1

                There are languages where “positive number” can be encoded at the type system level and cause compilation errors. And it’s not just dependently typed languages. In Ada it is trivial for example.

                1. 1

                  And no doubt there are some form of cast where the programmer can say “shutup compiler, I know what I’m doing”….. even when I don’t! Even if the idiot has to print to a string and read it back. (Seen it before, the stupidity of humanity has no limit)

            3. 3

              Author buries the lede here a bit in that the dopeness of the associated t-shirt is completely unmentioned in the title.

              https://teespring.com/shop/undefined-behavior-shirt

              1. 2

                That shirt is awesome! Might even trick programmers that see it into learning to avoid it. They Google it but find no rainbows. ;)