5 ms·
Because everything smaller than an int is usually promoted to int. int is the 'word' in C that things are calculated in. Even character constants are ints, all
by veltas 4y ago
Because everything smaller than an int is usually promoted to int. int is the 'word' in C that things are calculated in. Even character constants are ints, all enum constants are ints, the default type was int when default types were still a thing.
- jefftk 4y agoBut why not turn u16 into u32? Why switch it to being signed on promotion?
- rwmj 4y agoI'm sure the answer is going to be along the lines of "because PCC did that and they standardized it" :-/ Here's a fun standardization problem I came across recently (nothing to do with C): http://mywiki.wooledge.org/BashFAQ/105 http://mywiki.wooledge.org/BashFAQ/105
- veltas 4y agoBelieve it or not, this makes the behavior more like what you'd expect in many cases. For example: uint8_t x = 4; extern volatile uint64_t *reg; *reg &= ~x; In the last statement x is promoted to an int, and then when the logical NOT occurs every bit is set to 1, including the high bit. When it's converted to a uint64_t for the AND, the high bits are also set to 1. So the result is that the final statement clears only bit 2 in *reg. If it promoted to unsigned int, then it would also clear bits 32-63.
- codeflo 4y agoI don’t find that very convincing. It’s simply ambiguous, I might want this: *reg &= ~(uint64_t)x; or, and there’s no elegant way to even write this in C, I might want: *reg &=(uint64_t)(uint8_t)~x; The fact that I have to write two casts here to undo the damage of the auto-promotion is evidence of how broken this is.
- veltas 4y agoThat second line can be written: *reg &= (uint8_t)~x; Or: *reg &= ~x & 0xFF;
- tialaramex 4y agoextern volatile uint64_t *reg; *reg &= ~x; People should stop doing this. What this means is: extern volatile uint64_t *reg; uint64_t tmp = *reg; tmp &= ~x; *reg = temp; But of course when you write that chances are somebody will point out that you're running in interruptible context sometimes in this function, so that's actually introducing a race condition. Why didn't they say so when you wrote it your way? Because that looked like a single operation and so it wasn't obvious it might get interrupted.
- ynfnehf 4y agoANSI describes why in their rationale: https://www.lysator.liu.se/c/rat/c2.html#3-2-1 https://www.lysator.liu.se/c/rat/c2.html#3-2-1 . The unsigned preserving rules greatly increase the number of situations where unsigned int confronts signed int to yield a questionably signed result, whereas the value preserving rules minimize such confrontations. Thus, the value preserving rules were considered to be safer for the novice, or unwary, programmer. After much discussion, the Committee decided in favor of value preserving rules, despite the fact that the UNIX C compilers had evolved in the direction of unsigned preserving.
- edflsafoiewq 4y agoYes. The model C has is that the CPU has an ALU that that operates as (word,word)->word with int being the smallest word size. This explains many of C's conversion rules: to operate on a single integer, it first has to be promoted to a word size; to operate on two integers, they first have to be converted to a common word size, etc.
- lifthrasiir 4y agoAnd that makes writing a correct and portable C futile. Yes, defined-size types are a thing and I almost exclusively use them, but since the size of `int` itself is unknown and integer promotion depends on that size, the meaning of the code using only defined-size types can still vary across platforms. intN_t etc. are only typedefs and do not form their own type hierarchy, which in my opinion is a huge mistake.