6 ms·
I agree with most, and most of the others I might quibble with, but accept. However, the item to not use unsigned types is vastly stupid! Signed types have far
by gavinhoward 2y ago
I agree with most, and most of the others I might quibble with, but accept.
However, the item to not use unsigned types is vastly stupid! Signed types have far more instances of UB, and in the face of 00UB [1], that is untenable.
It is correct that mixing signed and unsigned is really bad; don't do this.
Instead, use unsigned types for everything, including signed math. Yes, you can simulate two's complement with unsigned types, and you can do it without UB.
On my part, all of my stuff uses unsigned, and when I get a signed type from the outside, the first thing I do is convert it safely, so I don't mix the two.
This does mean you have to be careful in some ways. For example, when casting a "signed" type to a larger "signed" type, you need to explicitly check the sign bit and fill the extension with that bit.
And yes, you need to use functions for math, which can be ugly. But you can make them static inline in a header so that they will be inlined.
The result is that my code isn't subject to 00UB nearly as much.
[1]: https://gavinhoward.com/2023/08/the-scourge-of-00ub/ https://gavinhoward.com/2023/08/the-scourge-of-00ub/
- nwellnhof 2y agoIn the vast majority of cases, integer overflow or truncation when casting is a bug, regardless whether it is undefined, implementation-defined or well-defined behavior. Avoiding undefined behavior doesn't buy you anything. If you start to fuzz test with UBSan and -fsanitize=integer, you will realize that the choice of integer types doesn't matter much. Unsigned types have the benefit that overflowing the left end of the allowed range (zero) has a much better chance of being detected.
- gavinhoward 2y ago> Avoiding undefined behavior doesn't buy you anything. This is absolutely false. Say you want to check if a mathematical operation will overflow. How do you do it with signed types? Answer: you can't. The compiler will delete any form of check you make because it's UB. (There might be really clever forms that avoid UB, but I haven't found them.) The problem with UB isn't UB, it's the compiler. If the compilers didn't take advantage of UB, then you would be right, but they do, so you're wrong. However, what if you did that same check with unsigned types? The compiler has to allow it. Even more importantly, you can implement crashes on overflow if you wish, to find those bugs, and I have done so. You can also implement it so the operation returns a bit saying whether it overflowed or not. You can't do that with signed types. > If you start to fuzz test with UBSan and -fsanitize=integer, you will realize that the choice of integer types doesn't matter much. I do this, and this is exactly why I think it matters. Every time they report UB is a chance for the compiler to maliciously destroy your hard work.
- nwellnhof 2y ago> You can't do that with signed types. What? Of course you can. If you want to add ints a and b: if (b >= 0 ? a > INT_MAX - b : a < INT_MIN - b) printf("overflow\n");
- gavinhoward 2y agoOkay, that's addition. Now do it for multiplication.
- lelanthran 2y ago> In the vast majority of cases, integer overflow or truncation when casting is a bug, regardless whether it is undefined, implementation-defined or well-defined behavior. Avoiding undefined behavior doesn't buy you anything. With respect, this is nonsense. With UB, the compiler might remove the line of code entirely. With overflow/underflow/truncation, the results are well-defined and the compiler is not allowed to simply remove the offending line.
- mcinglis 2y agoAuthor here, 10 years later -- I agree. I'd remove that rule wholesale in an update of this guide. Unsigned integer types can and should be used, especially for memory sizes. I would still advocate for large signed types over unsigned types for most domain-level measurements. Even if you think you "can't" have a negative balance or distance field, use a signed integer type so that underflows are more correct. Although validating bounds would be strictly better, in many large contexts you can't tie validation to the representation, such as across most isolation boundaries (IPC, network, ...). For example, you see signed integer types much more often in service APIs and IDLs, and I think that's usually the right call.
- gavinhoward 2y agoI think with those changes, my disagreement would become a mere quibble. > I would still advocate for large signed types over unsigned types for most domain-level measurements. Even if you think you "can't" have a negative balance or distance field, use a signed integer type so that underflows are more correct. I agree with this, but I think I would personally still use unsigned types simulating two's complement that gives the correct underflow semantics. Yeah, I'm a hard egg.