4 ms·
I know he's trying (badly) to make some point about undefined behaviour and/or strict reading of the C standard, but his answers are simply wrong. The first ca
by rwmj 3y ago
I know he's trying (badly) to make some point about undefined behaviour and/or strict reading of the C standard, but his answers are simply wrong. The first case, for example, returns 8 [edit: the size of the structure] (not "I don't know") under any reasonable compiler, and isn't even UB. The second one returns 0, and is also not UB. etc.
Also if someone wrote this bit of obscurantism in any programming language it should be rejected hard when reviewing the code:
return (((((i >= i) << i) >> i) <= i));
- amalcon 3y agoThat's an illustrative example. You're not meant to actually use such code, but the relevant insight (that code with binary shifts is rarely portable with types of unspecified size) is accurate.
- nuc1e0n 3y agoI understand where you're coming from, but have you tried them? I think there's a good chance the first question will return 16 on 64 bit systems.
- tyingq 3y agoReturns 3 on Borland Turbo C, which I think most people would say is a reasonable compiler.
- matja 3y agoDepends on the ABI which the compiler targets. The vast majority of 64-bit OS's are ILP32, LLP64 or LP64, where ints are 32-bit - but to the paper's point, you don't know until you compile it.
- dzaima 3y ago"are simply wrong" and "under any reasonable compiler" don't mix - yes, for arm(32/64)/x86(-32 or -64) there are common values, but that doesn't make everything else not exist. OP doesn't say that the first two are UB, just that you don't know what they will be without additional information. For example, here's a GCC-supported target that's available on Compiler Explorer, where the result of question 1 is "3", and question 2 is "1": https://godbolt.org/z/MjT8YhrTc https://godbolt.org/z/MjT8YhrTc
- rwmj 3y agoSure, that's an AVR platform where structs are packed and ints are 16 bits, and it returns the size of the struct, in other words, the correct answer. And that's my point, it returns the size of the struct, it's not "don't know", it's not doing anything weird, so why have the question?
- dzaima 3y agoBut, thus, upon seeing the program, without additional information you cannot know whether it'll give 8 or 3 (or who knows what else), and thus "I don't know." is very much an appropriate answer. Of course, it might be more clear to say "it depends" or "there isn't enough information to answer", but I'd say "don't know" is nevertheless a correct answer. Definitely more correct than "8" at least. "it's not doing anything weird" only to people who do already know the C standard inside out. But there is a significant amount of people who might not know everything C (or might know that there are some weird things about types, but still assume 'int' will be at least 4 bytes or something). It's of course not a question of much practical impact (for anyone not working in embedded at least) but it's nevertheless one that can be at least interesting to some.
- rwmj 3y agoThe only time you're writing sizeof(x) is when you want to know the size of 'x', eg. to store it somewhere else or zero out the memory or something of that sort. And it gives the right answer, great! It doesn't ever do something that's undefined or strange, and it's not an obscure part of the C language.
- Buttons840 3y agoYou originally said: > The first case, for example, returns 8 under any reasonable compiler That is wrong. This part of C is apparently obscure enough that people make false assumptions like your own I quoted. After you made that claim, someone provided a case where GCC returns something other than 8 and you edited your statement. Again, your statement was incorrect until you edited it, and so there must be some obscurity involved.
- richardjam73 3y agoI got the first example to give 4 and the second one returns 1. OpenWacom DOS 16Bit. Under 32bit it gives 8 AND 0.
- mannykannot 3y agoMany errors have arisen from someone tacitly assuming what "any reasonable compiler" would do. These questions barely scratch the surface of the possibilities. Furthermore, just because your code works with Reasonable Compiler 1, it does not mean it will work with Reasonable Compiler 2. I think I have seen enough C compilers that, for each of these questions, you could pick a pair giving different answers.
- AnimalMuppet 3y agoWorse, Reasonable Compiler 1 may give different results, depending on what flags you pass it at compile time.
- krychu 3y agoYour code dictates the compiler's behavior. If you grant the compiler flexibility, it's unreasonable to complain about unexpected results.
- mannykannot 3y agoI don't think it is a complaint; it's an observation in support of a point of view.
- nanolith 3y agoIt's fine to bristle about underhanded evangelism (e.g. "Use Rust instead of C, because C is a foot cannon and Rust is perfect"), but that's definitely not the case here. This is a reasonable critique of the common C pitfalls. I use C daily. The answer in each category is "I don't know" because, without the additional information provided by a specific ABI, specific target, and specific compiler, these are all undefined. No language is perfect. It's important to understand the limitations of a language to use it effectively. That is closer to the author's point. This is not a condemnation of C, but rather, an acknowledgement of C, warts and all. The original point of C was to provide a portable low-level language that could capture expressions instead of writing everything in assembler, so that UNIX could be ported to platforms other than the PDP-11. It's also a reason why C still has uses not shared by newer languages, because these newer languages are more precise. This is a Good Thing, but unfortunately, developers who are unaware of this imprecision can make mistakes that aren't portable and may not behave as they assume. These problems appear, seemingly out of nowhere, when porting source code to a new compiler or a new platform. While the examples the author provides are over-the-top and easy for an experienced C developer to catch, there are more subtle examples, such as promotion and signed integer overflow, that are hard even for an experienced C developer to catch. This is why I use static analysis, model checking, proof assistants, and even a modern compiler with built-in pedantic checking against the standard. This language has subtle corner cases that even someone with 25-30 years of experience can miss. Unit testing can miss these corner cases unless the right input values are chosen, and the right input values are not always so easy to guess. It's not a condemnation of C or an attempt to evangelize newer languages to point this out, but rather, an acknowledgement that mastery of any language means accepting both its strengths and limitations. C has both.
- cuanim 3y agoHello, what would be your recommendations for someone who wants to take the next step from basics of C to contributing to FOSS repos (for ex: FFMPEG or VLC or mpv)? I have been working my way through [Modern C](https://inria.hal.science/hal-02383654/file/ModernC.pdf https://inria.hal.science/hal-02383654/file/ModernC.pdf) but I can't seem to figure out how does one start with making(/contributing to) non trivial projects.
- virtuous_sloth 3y agoYou can't say that with certainty without specifying the compiler, it's version, and the architecture.
- P-Nuts 3y agoI have debugged a problem that was due to a left shift of 1ULL by 64 (in a routine that tried to set the lowest n bits by shifting 1 left and subtracting by 1). I needed to read the Intel docs to find out what actually happens to work out how badly we’d corrupted the customer data and how to write a fix to unmangle it. What Intel processors do is ignore the upper bits of the shift quantity, so it’s effectively (1ULL << (x % 64)) - 1, therefore the function that should have set all ones for x=64 actually set all zeros.