10 ms·
Summary of C/C++ integer rules
- dreinhardt 6y agoIt’s funny that you would end up with a similar conclusion for other parts of the language (e.g. operators) as well. Just a gigantic set of inane rules everywhere causing you to constantly be in danger of introducing bugs and portability issues.
- Joker_vD 6y agoBut! And that's important -- it allows for great performance, so you can make ten/hundred times more mistakes per second than in other, "safer" languages.
- tammerk 6y agoNowadays, it doesn't provide any performance gain. I didn't see those days but maybe it was important for performance back in 70s/80s/90s even it was risky? e.g null terminated string was chosen due to low space overhead.
- creato 6y agoIt depends on what you are doing. For some kinds of programs, C/C++ are going to be much faster than most "modern" languages.
- tammerk 6y agoI didn't mean C is not fast or not faster than other languages. It's still the fastest one I believe. What I meant is undefined behaviors allow compilers to optimize in a way that would not be possible otherwise. So, it might be a deliberate decision back then, to leverage performance. I don't know, just an idea.
- jjgreen 6y agoIt used to be "folk knowledge" that only Fortran and hand-crafted ASM were faster. Not sure if that's still (or ever was) true.
- deleted 6y ago[deleted]
- hajile 6y agoI guess it was maybe true one time. http://www.catb.org/jargon/html/story-of-mel.html http://www.catb.org/jargon/html/story-of-mel.html
- tammerk 6y agoI agree but I didn't consider these while saying C is the fastest. These are not "general purpose", like you don't write your db, browser, http server or game engine with these.
- nicoburns 6y agoMost, but not all. Languages like Rust and Zig show that you can have the performance without the landmines.
- hajile 6y agoAlso, theoretical performance is overrated. Almost all the things that lends themselves to speed make code brittle and incapable of future modification. Once you’ve got your C code doing safety checks with data types that won’t break under the littlest change, the code becomes much slower than code golf would suggest. A common example is passing void pointers everywhere. You either check every call every time (aka dynamic typing) or rush everything on the idea that the programmer understands the system completely and never forgets or messes up. Better types give you all the speed AND all the safety here.
- lifthrasiir 6y ago> it allows for great performance, so you can make ten/hundred times more mistakes per second than in other, "safer" languages. This is false. For a long time C performance used to be inferior to Fortran, which is arguably safer than C. It's hilarious that the strict aliasing and `restrict` keyword was born out of making C on par with Fortran and UB became a major issue to C programmers as a result!
- atkwarriors 6y agoYes, that's why C has undefined behavior. Absolutely
- RMPR 6y agoIt's a feature, not a bug.
- tammerk 6y agoIt's more funnier that although language is full of traps, in practice it works quite well. I don't think any C developer(or let's say %95) knows all the rules mentioned in the article, yet we are still one piece. Does anybody know any paper for bugs per lines of code for different languages or something similar?
- nayuki 6y agoC/C++ developers not knowing the rules does bite them. For example, it made the 32-bit/64-bit transition much more painful. See https://www.viva64.com/en/a/0004/ https://www.viva64.com/en/a/0004/ ; https://www.viva64.com/en/a/0065/ https://www.viva64.com/en/a/0065/
- bregma 6y agoIt discouraging. If the language requires you actually know what you're doing you can't hire dirt-cheap easily-replaced code monkeys to bang out your ideas and the end result is you get to keep less of the investors' money for yourself.
- AndriyKunitsyn 6y agoIt can feel good to imagine yourself an enlightened master among code monkeys, yet on practice, everybody can be a code monkey sometimes, and when this happens in C/C++, it will leave a ticking time bomb in the codebase, that will lay there until a customer blows up on it, no matter how many millions went into QA of the product. And on practice, C/C++ developers are among lower-paid programmers - probably because "banging out ideas" and producing actual programs that actually work, are valued more than language elitism.
- bigcorp-slave 6y agoIt’s actually not true at all that C++ developers are lower paid. Rather, their pay is highly bimodal. Most work at all FAANGMULA companies is C++.
- saagarjha 6y ago> Having signed and unsigned variants of every integer type essentially doubles the number of options to choose from. This adds to the mental burden, yet has little payoff because signed types can do almost everything that unsigned ones can. Unsigned types are quite useful when doing bit twiddling because they don't overflow or have a bit taken up by the sign.
- dreinhardt 6y agoWhich is why sane languages of the time had a bitfield type.
- enriquto 6y ago> Unsigned types are quite useful when doing bit twiddling because they don't overflow or have a bit taken up by the sign. That's essentially their only application. The rest are stupid single-bit memory-size optimizations. As Jens Gustedt noted, it's one of the (many) misnomers in the C language. It should be better called "modulo" instead of "unsigned". Other such misnomers that I recall: unsigned -> modulo char -> byte union -> overlay typedef -> typealias const -> immutable inline -> negligible static -> intern register -> addressless EDIT: found the reference https://gustedt.wordpress.com/2010/08/18/misnomers-in-c/ https://gustedt.wordpress.com/2010/08/18/misnomers-in-c/
- dkersten 6y ago> That's essentially their only application. What about when it doesn't make semantic sense to have negative values? Eg for counting things, indexing into a vector, size of things. If negative doesn't make sense, I use unsigned types. Its not about the memory-size in that case.
- enriquto 6y agoPositive values are a particular case of signed values, you can still use signed ints to store positive values. No need to enforce your semantics through type, and especially not when the values of the type are trivially particular cases of the values of another type. For example, when you write a function in C that computes prime factors of an int, do you need a type for prime numbers? No, you just use int. The same thing for positive numbers, and for even numbers, and for odd numbers. You can and should do everything with signed integers, except bitfields, of course.
- RMPR 6y agoWhat a coincidence this gets posted today, I posted something[0] a couple of hours ago about how specifically a combination of these rules can bite you very hard. 0: https://rmpr.xyz/Integers-in-C/ https://rmpr.xyz/Integers-in-C/
- MaxBarraclough 6y ago[Dons language lawyer hat] > floating-point number types will not be discussed at all, because that mostly deals with how to analyze and handle approximation errors that stem from rounding. By contrast, integer math is a foundation of programming and computer science, and all calculations are always exact in theory (ignoring implementations issues like overflow). Integer overflow is no mere implementation issue, any more than errors are an implementation issue with floating-point. > Unqualified char may be signed or unsigned, which is implementation-defined. > Unqualified short, int, long, and long long are signed. Adding the unsigned keyword makes them unsigned. There's an additional point here that's not mentioned: char, signed char, and unsigned char are distinct types, but that's only true of char. That is, signed int describes the same type as int. You can see this using the std::is_same type-trait with a conforming compiler. Whether char behaves like a signed integer type or an unsigned integer type, depends on the platform. > Signed numbers may be encoded in binary as two’s complement, ones’ complement, or sign-magnitude; this is implementation-defined. This is no longer true of C++. As of C++20, signed integer types are defined to use two's complement. [0] I don't think C intends to do the same. > Character literals (in single quotes) have the type (signed) int in C, but (signed or unsigned) char in C++. That's not correct. In C++, the type of a character literal is simply char, never signed char nor unsigned char. As I mentioned above, whether char is signed depends on the platform, but it's always a distinct type. > Signed division can overflow – e.g. INT_MIN / -1. This isn't just overflow, it's undefined behaviour. > Counting down > Whereas an unsigned counter would require code like: > for (unsigned int i = len; i > 0; i--) { process(array[i - 1]); } That's one solution, but it might be a good place for a do/while loop. [0] https://stackoverflow.com/q/57363324/ https://stackoverflow.com/q/57363324/
- dataflow 6y ago>> Character literals (in single quotes) have the type (signed) int in C, but (signed or unsigned) char in C++. > That's not correct. In C++, the type of a character literal is simply char, never signed char nor unsigned char. I'd assume the author meant (signed `char` | unsigned `char`) rather than (`signed char` | `unsigned char`).
- 6y ago
- flohofwoe 6y agoIn the myths section: > char is always 8 bits wide. int is always 32 bits wide > Signed overflow is guaranteed to be wrap around. (e.g. INT_MAX + 1 == INT_MIN.) Are there any current, relevant hardware architectures where this is not true (e.g. bytes are not 8 bits, and integers are not 2's complement)? E.g. what's the point of "portability" if there is no physical hardware around anymore where those restrictions would apply?
- nemetroid 6y agoSigned integers wrapping and 2’s complement are separate issues. C++20 specifies that signed integer are 2’s complement, but signed overflow is still undefined.
- AshamedCaptain 6y agoRemember to add: that can actually run standard C++ (i.e. with exceptions)? Certainly you can find an architecture which may run some type of C-like language with strange arithmetic rules (e.g. DSPs). I would bet it's harder to find one such architecture where one can run standard C, and impossible to find one which can run standard C++.
- lultimouomo 6y agoThis. I don't understand why everyone must suffer the pain of the possibility of weird char widths instead of just settling on using a non-standard C in a bunch of DSPs. It's not like you're going to link a bunch of regular run of the mill C libraries on them anyway.
- not_knuth 6y agoIsn't this an artifact of the age of C? When it was first created it was a major concern to support every architecture, so they put it in the standard. I don't think anyone has wanted to go through the pain of removing it ever since. After all, who are language nerds to dictate chip manufacturers what the ISA should look like? :P And it was only in the last 2 decades that everything got dominated by x86...
- criddell 6y agoThis is one of the misconceptions: > sizeof(T) represents the number of 8-bit bytes (octets) needed to store a variable of type T. That's a misconception I had and I've never run into a problem. What's a platform where sizeof works differently? Also, what's the reasoning for sizeof to be an operator rather than a function?
- GlitchMr 6y agoSee https://stackoverflow.com/questions/2098149/what-platforms-have-something-other-than-8-bit-char https://stackoverflow.com/questions/2098149/what-platforms-h.... As for `sizeof` being an operator, well, C doesn't have generics, so it has no choice but to make `sizeof` somehow special. If you don't want to bother supporting platforms where byte is not 8-bit (a reasonable choice I would say), use `int8_t`/`uint8_t` instead. Those types won't exist on platforms that don't have 8-bit bytes.
- MaxBarraclough 6y ago> C doesn't have generics, so it has no choice but to make `sizeof` somehow special It could have used a different syntax though. Ada has a special syntax for compile-time inquiries like this, so there's no way to confuse them with function calls. Ada calls these attributes. https://en.wikibooks.org/wiki/Ada_Programming/Attributes#Language_summary_attributes https://en.wikibooks.org/wiki/Ada_Programming/Attributes#Lan...
- masklinn 6y ago> If you don't want to bother supporting platforms where byte is not 8-bit (a reasonable choice I would say), use `int8_t`/`uint8_t` instead. Those types won't exist on platforms that don't have 8-bit bytes. You'll have the issue that, as one of the commenters explained above, `char` is its own thing, independent and separate from `signed char` and `unsigned char` to say nothing of `int8_t` and `uint8_t`. This means that while you can use your own thing for your own functions you can not do so if your values have to interact with libc functions (or most of the ecosystem at large). If you only want to support platforms using 8-bit chars, you should check CHAR_BIT. That is actually reliable and correct.
- dvfjsdhgfv 6y ago> Python only has one integer type, which is a signed bigint. Compared to C/C++, this renders moot all discussions about bit widths, signedness, and conversions – one type rules all the code. But the price to pay includes slow execution and inconsistent memory usage. Well, the beauty of C is that you can have that too, if you wish, and you have many options to choose from.
- dahfizz 6y agoOne thing I think should have been mentioned: size_t is guaranteed to be large enough to index all of memory, which is why it is the return type of size of.
- dusanz 6y agosize_t is only guaranteed to be large enough to store the size of the largest object. This is not the same as being able to index all of memory. You could imagine a platform with restricted continuous allocation size where the maximum object size is smaller than the size of the address space.
- nayuki 6y agoFurthermore, size_t bears no relationship with int; it could be wider/equal/narrower. If size_t is narrower than int, then doing any arithmetic on size_t variables will result in automatic promotion to signed int, which can lead to dangerous signed overflow. C/C++ are full of footguns.
- lifthrasiir 6y ago> Signed numbers may be encoded in binary as two’s complement, ones’ complement, or sign-magnitude; this is implementation-defined. Thankfully, in addition to what MaxBarraclough helpfully pointed out, every (u)intN_t type provided by <stdint.h> is guaranteed to use two's complement even in C99.