8 ms·
A Quiz About Integers in C
- matzahboy 14y agoI believe that the answers are incorrect. They assume that all true boolean expressions evaluate to 1, whereas the c standard only guarantees that they don't evaluate to 0. Edit: As pointed out by the responses, I am wrong
- aidenn0 14y agoNot true, !0 is always 1 in the C standard. Other parts are wrong though (they assume short is narrower than int, and that int is 32 bits).
- matzahboy 14y agoThanks. I didn't know that
- rcfox 14y agoThe assumptions are explicitly stated at the beginning of the quiz.
- chc 14y agoIt's not wrong. If you read the intro, they note that those traits are implementation defined, so they say to assume you're using an implementation with those characteristics.
- nooop 14y agoSo this is incorrectly titled. This should be a "A Quiz About Integers in C on common x86 and x64 ABI." Unfortunately this does not educate people to that kind of issues, which is very unfortunate.
- rcfox 14y ago6.3.1.2 Boolean type "When any scalar value is converted to _Bool, the result is 0 if the value compares equal to 0; otherwise, the result is 1" http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf page 43
- cliffbean 14y agoNo, it guarantees 0 or 1; see C99 6.5.8 paragraph 6: these operators "shall yield 1 if the specified relation is true and 0 if it is false."
- jstanley 14y ago"the c standard only guarantees that they don't evaluate to 0" Why do people who haven't checked the C standard make such claims? My C lecturer made exactly the same mistake.
- deleted 14y ago[deleted]
- prophetjohn 14y agoWhat is the reasoning behind the fact that, say, (INT_MAX + 1) != INT_MIN?
- Tuna-Fish 14y agoAt the time of the introduction of C, there were plenty of different architectures that used different kinds of signed integer representations. C was evolved to be the "lowest common denominator", so things that operated differently on different hardware were made undefined to preserve portability.
- vilda 14y agoFair to say the number of architectures increased since the introduction of C...
- rpearl 14y agoTwo's complement is not required; there might be systems that use saturating signed arithmetic for example.
- matheusalmeida 14y agoThat is true but it's not the reason... INT_MAX + 1 must be 0 for unsigned int because the C standard defines wrap-around behavior for unsigned data types. Saturated data types are only defined in Embedded C. E.g.: Sat Fract
- jacquesgt 14y agoThat is the entire reason. Because the representation of signed numbers could vary from implementation to implementation, making INT_MAX + 1 (or any signed integer overflow) undefined allows for more efficient generated code. If it were defined in some way, the generated code would need checks for overflow wherever it was possible, which would lead to bigger and slower code. INT_MAX + 1 is never 0 for unsigned int. (unsigned)INT_MAX + 1 is equal to INT_MAX + 1. You're thinking of UINT_MAX + 1, which is always 0.
- forgotusername 14y agoWhile I didn't expect to do well on this (if for no other reason, then lack of parenthesis to demarcate evaluation order!), however could someone please explain why CHAR_MAX == SCHAR_MAX? My understanding is that no such guarantee exists for the range of these types. A few of the questions seemed odd, or mixing implementation specifics with standardize. Edit: s/width/range/
- jacquesgt 14y agoOn the example platforms described at the top of the quiz, CHAR_MAX (char max) is 127, which SCHAR_MAX (signed char max) is also 127. This is because chars are signed by default on those platforms. The point being made is that the default signedness of char can be different from platform to platform, and the default can be confusing to people who associate the char type with binary data. Better to use int8_t and uint8_t when you want a portable signed or unsigned 8-bit number. int8_t is always signed, and uint8_t is always unsigned.
- sltkr 14y agoThat tripped me up too, and I'm pretty sure that answer is wrong. It's implementation defined, since it's implementation defined whether or not 'char' is signed or not.
- delan 14y agoIt is, but this quiz is in the context of x86 and x86-64. > Also assume that x86 or x86-64 is the target. In other words, please answer each question in the context of a C compiler whose implementation-defined characteristics include two's complement signed integers, 8-bit chars, 16-bit shorts, and 32-bit ints. The long type is 32 bits on x86, but 64 bits on x86-64 (this is LP64, for those who care about such things).
- Teckla 14y agoAs a long-time C programmer (20+ years), this quiz -- despite its flaws -- are a good example of how difficult it really is to write 100% portable C code. I often hear developers talking about how C is a simple language, but it may or may not be, depending on how you define "simple". For example, a written alphabet with only two letters -- A and B -- is "simple" to grasp (there's only two letters to learn!), but it would be very difficult, in the real world, to read and write such a language. I think C is similar: "simple" in a sense, but very, very complex in another sense. C could use some massive clean-up, in my opinion. Much of its design was so that it could be ported to CPUs which haven't really existed in the wild in a long time in great enough numbers to warrant all the undefined and implementation dependent behavior. The language could be hugely simplified, in the usage sense, if much of the cruft were jettisoned.
- AngryParsley 14y agoThat's a huge problem with programming languages: Designers can always add things, but removing stuff is hard. JavaScript is a great example of this. It still has automatic semicolon insertion, variable hoisting, and a broken comparator (==). Practically everyone agrees these are bad things, but they can't be changed. Doing so would break backwards compatibility, and there's a huge ecosystem of JavaScript programs that would need to be updated. Also, language users often rebel when language designers make breaking changes. Python 3 tried to remove cruft, and look how slowly it's been adopted. Instead of adopting 3.x, people backported the features they wanted to 2.x. I would love it if C had less cruft, but when I say that I mean, "I want C with less cruft, but with the same huge ecosystem of documentation and libraries and tools and debuggers and profilers that crufty-C has."
- taliesinb 14y agoThat's why I think the language Go is such a great development. The authors seem to have added only the minimum set of features that make the language workable for their initial needs. As a result, these features are largely orthogonal, and the rules are simple to state and learn -- even if they at first seem a bit odd (you have to cast all numeric types to each-other before they can interact). In contrast to Scala, another modern language I investigated recently, which seemed like quite a thicket of features.. some of which seemed just to be there to mediate the interaction of other features.
- wging 14y agoThis might be a naive question. On Question 12 ("Assume x has type int. Is the expression x<<32..."), why is this considered an error? Why do we want a compiler to prefer this over "x<<n means shift x by (n % sizeof(int))"?
- nitrogen 14y agoOne possible reason: it makes little sense for 1 << 33 to be less than 1 << 31 but not zero (assuming 32-bit ints -- replace 31 and 33 with 63 and 65 for 64-bit ints). In other words, if you keep shifting a number to the left, it should keep growing, until all the bits are gone. Another possible reason: CPUs have instructions for implementing << as it now stands.
- wging 14y agoAh, I see... 'Shift' doesn't mean what I thought it meant. Somehow I thought that a left shift was supposed to rotate the number. E.g. shifting by 1 would shift the 31st bit into the 0th bit.
- pdw 14y agoOn x86, the shift instruction only looks at the bottom 5 bits of the shift distance. x<<32 effectively is interpreted as x<<0. (I'm sure other architectures have the same restriction.)
- JulianMorrison 14y agoIsn't the real answer to these questions "don't do that, you fool"?
- antirez 14y agoin theory yes, but often there are cases where you can't avoid mixing signed and unsigned stuff for example. Think about write() and read() syscalls that will return a signed type, but are often used in a context where buffer lengths are expressed in unsigned types.
- JulianMorrison 14y agoThen you probably want to mark it with an explicit function named something like "int32_to_uint32()" which you have defined (and debugged) in one place that performs the appropriate conversion with sanity checks. Basically, IMO, C's implicit conversions are a flaw in the spec. A more sensible language would require every conversion (aside from those involving un-dimensioned constant data) to be spelled out explicitly. Since you can't make C sensible, the next best thing is to scrupulously isolate the stupid parts.
- CodeMage 14y agoI think that answer deserves partial credit ;) I believe that the reason people need to know these things is not because they should be playing Russian roulette with them, but because they need to know what's going on when things go accidentally wrong.
- cjensen 14y agoSo in question 5, the C Standard answer is rejected because the x86-specific answer is required. But then in question 7, the x86 (and all normal archs) answer is rejected and the C Standard answer is required. Would it really kill you to decide on the questionnaire rules before writing the questions? :-)
- delan 14y agoThere's a difference between 'implementation-defined' and 'undefined'. Question 5 is implementation-defined behaviour, while question 7 is undefined behaviour.
- klodolph 14y agoI was really surprised how many errors there were in the quiz. Errors in the quiz: 3. (unsigned short)1 > -1: This will evaluate to 0 on systems where sizeof(short)<sizeof(int), and 1 on systems where sizeof(short)==sizeof(int). Yes, such systems exist. Old Cray supercomputers and various DSP processors don't have byte-addressable memory, only word-addressable, so they make all primitive types a word long. 5. SCHAR_MAX == CHAR_MAX: The person who wrote the quiz even ACKNOWLEDGES that the quiz is incorrect here and apologizes. This is implementation-dependent, it is 1 when char is signed and 0 when char is unsigned. Both types of systems exist. You can use -funsigned-char on GCC, for example. 11: int x; x << 31: This is only defined for some values on platforms where int has at least 32 bits. There exist systems where int has 16 bits, in which case this is undefined for all values. Old DOS PCs often used 16-bit ints, and I believe that ints were 16 bits when C was invented. 12: int x; x << 32: This is only undefined on systems where int has no more than 32 bits. There exist systems where int has more bits. Do a search for ILP64 if you wish to hear about such systems. 14: unsigned x; x << 31: Again, this is only defined on systems where int has at least 32 bits. See #11. 15: unsigned short; x << 31: This one is tricky. There are four different cases, depending on the size of int and whether sizeof(short)==sizeof(int). 15a: sizeof(short) == sizeof(int): defined for all x, since x gets promoted to unsigned. 15b: sizeof(short) < sizeof(int), int has less than 32 bits: defined for no x. I don't think any such systems exist. 15c: sizeof(short) < sizeof(int), int has at least 32 bits but less than 32 more than a short: defined for some x. This is the most common, with 16-bit short and 32-bit int. 15d: sizeof(short) < sizeof(int), int has at least 32 more bits than a short: defined for all x. This is the uncommon ILP64 system. 18: int x; (short)x + 1: This is outright incorrect. Casting int to short results in undefined behavior if the value cannot be represented as a short. Truncation is only guaranteed to occur for unsigned types.
- DCoder 14y agoThe intro text explicitly defines those datatype sizes: In other words, please answer each question in the context of a C compiler whose implementation-defined characteristics include two's complement signed integers, 8-bit chars, 16-bit shorts, and 32-bit ints. The long type is 32 bits on x86, but 64 bits on x86-64 (this is LP64, for those who care about such things).
- deleted 14y ago[deleted]
- CamperBob2 14y agoQuestion 4 is based on completely false premises ("On x86-64, int is shorter than long.") That's as far as I bothered to go, since the author is clearly confused about what features are language-dependent versus compiler-dependent versus architecture-dependent. To be fair, it's a confusing business, which is why I religiously avoid using unsigned types in C.