7 ms·
These examples do look scary. However, in my 16 years of experience of mostly writing In C, I have never found a single chance that I need to actually use the e
by up2isomorphism 4y ago
These examples do look scary. However, in my 16 years of experience of mostly writing In C, I have never found a single chance that I need to actually use the expressions that I got the answers wrong.
Never mix unsigned and signed integer comparison unless you know exactly what you are doing.
And never do arithmetic on boundaries definitions like INT_MAX, they are boundaries , why you need to compute a derived value on boundaries?
If you do not need arithmetic behavior, do not use signed integer, use unsigned. Because computer does not understand sign, so if you do not need a sign, do not use it.
You do not need to be a language committee member to write C , you just need to understand the reason behind its design.
- int_19h 4y agoIt's not always obvious that you're mixing signed and unsigned, though. I think the biggest source of these kinds of bugs is because size_t is unsigned but ptrdiff_t is signed. So the moment you mix pointer subtraction with sizeof, footguns abound.
- jcelerier 4y agoThe problem is that for unsigned one of the boundaries is 0, which is an extremely common number - there isn't a couple months where I don't find a bug due to a size-1 somewhere
- 10000truths 4y agoSubtraction would presumably fall under the arithmetic behavior that OP was talking about.
- eru 4y agoMaybe. But subtraction is also perfectly well defined for unsigned ints in C.
- kcexn 4y agoIf you wanted to be extremely careful, you might pass unsigned ints into a function by copy, and cast it as a signed int to perform arithmetic, then do a boundary check before returning a result to be assigned back to an unsigned integer. The unsignedness of the integer lets readers of the code know that it's a constant of some kind, and that comparisons should be performed with care. Normally, when I've worked with C (fairly limited), unsigned numbers normally get used as flags for bitwise comparisons. Sometimes your flags may have a partial ordering defined on them (so a sense of flag A being 'smaller' than flag B), so you might need some arithmetic to do those checks you need to be explicitly careful with the boundary checks though.
- pantalaimon 4y agoThe compiler will also warn about these cases where you better be explicit to avoid undesired behavior.
- tredre3 4y ago> If you do not need arithmetic behavior, do not use signed integer, use unsigned. Because computer does not understand sign, so if you do not need a sign, do not use it. That's interesting. In my career (embedded development) I've learned to do the opposite. Always use signed unless you have a reason not to. Even if a value can't naturally be negative, use signed. Use unsigned only if you need the extra bit, or if you're doing bitwise operations. > Because computer does not understand sign Computers understand signs just fine, we're long past the days of the 6502 N-flag being a glorified bit 7 check. All CPUs have signed instructions.
- WaffleIronMaker 4y ago> Always use signed unless you have a reason not to. Even if a value can't naturally be negative, use signed. Can you elaborate on what benefits this approach has? I would feel that, especially when a number cannot be negative, unsigned integers seem like a proper representation of the data?
- gugagore 4y agoHere is an example: https://wesmckinney.com/blog/avoid-unsigned-integers/ https://wesmckinney.com/blog/avoid-unsigned-integers/
- eru 4y agoA big problem is that C doesn't make you stick to the representation. If you accidentally subtract and go into 'negative' numbers, using an unsigned in C will just happily give you the wrong answer. Now in something like Haskell, it's much more robust advice to make invalid states unrepresentable. But C's compiler and runtime don't typically help you, when you are trying to break your representation.
- Quekid5 4y agoA couple of things I can think of off the top of my head: Since the compiler knows that certain things are UB using signed offers better (often significantly better) optimization opportunities then using unsigned. Plus, you get to use UBSan to detect bad code.
- bee_rider 4y agoIs it your experience that there are many projects where you can reliably say some variable will never need arithmetic behavior? I don't have 16 years of experience writing C, but > If you do not need arithmetic behavior, do not use signed integer, use unsigned does seem in a roundabout way to match the advice that I've gotten from other folks -- usually stick to signed (because you never know if somebody is going to want to use an integer in, say, a downwards loop). Your comment just seems to highlight the less usual case, where you can be sure that nobody will ever need that arithmetic behavior... maybe it depends on the type of applications, though.
- up2isomorphism 4y agoIt always depends on the area of focus. You can also consistently choose the default int type to signed while being fully aware of you lose half range but gain signed integer arithmetic. For me I am mostly doing system and networking so I tends to to unsigned. The key is to choose a consistent default while aware of that you are in a non-intuitive zone when that default type puts you in.
- me_me_me 4y agoFor me using uint8_t and family makes things simpler (working predominately with packets) It is a container for X bits and thats it, its easy to understand overflow and corner case behaviors. I rarely find good reason to use signed ints, so only when I need them i worry about sign handling. > because you never know if somebody is going to want to use an integer in, say, a downwards loop Why risk a possible bug, for a hypothetical case of someone wanting signed var? If they really want to use signed vars they can handle it themselves.
- bee_rider 4y agoYeah. My context was doing linear algebra stuff. It is apparently a known mistake to say "well we aren't going to have to describe negative positions for matrix elements, so why not make the integer unsigned?" but there are lots of cases where you want a negative offset for whatever reason. ("I want to stop right before the diagonal"). I can definitely see why you might not ever need a sign in networking stuff.
- Sirened 4y ago> And never do arithmetic on boundaries definitions like INT_MAX, they are boundaries I'd argue something stronger: if you care about boundaries like INT_MAX, you should never be comparing them using your regular comparison tools. I.e., even though there are correct ways to compute whether x + 1 will overflow, don't bother trying to do that and instead always use __builtin_add_overflow since you can't fuck it up. Trying to do these sorts of edge checks are incredibly hard and it has lead to numerous security vulnerabilities due to checks being optimized out. The builtins do exactly what they say and you don't have to worry about UB blowing your foot off.