9 ms·
Using signed or unsigned int by default in C (2015)
- w0mbat 4y agoRepost from 2015
- reltuk 4y agoSimilar arguments are also seen in https://graphitemaster.github.io/aau/ https://graphitemaster.github.io/aau/. When using unsigned ints for backward iteration, I'm partial to looping as: for (i = size - 1; i < size; i--) { ... } since it has so much symmetry with forward iteration.
- 10000truths 4y agoI would rather just use a plain for loop and manipulate the index in the loop body: for(uint32_t i = 0; i < length; ++i) { i = (length - 1) - i; // ... } Much easier for me to understand, making it less likely to mess anything up.
- edflsafoiewq 4y agoYou'll need a new variable there, but otherwise yeah.
- WalterBright 4y agoEven easier (for D): foreach (i; 0 .. length) D takes care of selecting the correct type for `i`.
- phkahler 4y agoI'd argue that is a terrible way to write it since it depends on the rollover to terminate. Using i >= 0 is less mental work but also requires that i be signed.
- ghoward 4y agoRollover on unsigned ints is well-defined in C.
- hbn 4y agoI think their point is that it's less well defined in the average human's brain When most people see 0 - 1 they don't immediately think 11111111111111111111111111111111
- xdavidliu 4y agoif we used i >= 0 and someone were to come in and change "int i" to "unsigned i" or "size_t i", the result would be catastrophic
- Leherenn 4y agoThe same argument can be applied to GP's code with the added bonus it will cause UB at some point.
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- JonChesterfield 4y ago`for (unsigned i = n; i-->0;) {}` Probably formatted i-- > 0. Concise and reads (once used to it anyway) something like i decrementing to zero.
- coding123 4y agouse longs because it's not arch dependent
- xdavidliu 4y agolong in C and C++ is absolutely arch dependent. In Java it's not depedendent, but neither are any of the other primitives in Java.
- acuozzo 4y agolong is only guaranteed by to be a minimum of 32 bits. It's 64 on Tru64 UNIX systems, for instance.
- messe 4y agolong is definitely arch dependent. It’s even operating system dependent: It’s 32-bits on 32-bit x86 unix-likes and windows, as well as 32-bit on 64-bit windows. On 64-bit unix-likes it’s 64-bit. Embedded is so varied (and generally 16 or 32 bit), that I’m not even go to hazard a guess on the distribution there.
- deleted 4y ago[deleted]
- qsort 4y agoDoesn't this depend? - When iterating an array, you're supposed to use "size_t", because it's the only type that's guaranteed to contain the maximum size of an array. - When storing the difference between two pointers, you're supposed to use "ptrdiff_t" because it's the only type that's guaranteed to contain the signed difference between two related pointers. - When storing numbers, prefer unsigned to signed because signed overflow is UB.
- FartyMcFarter 4y ago> - When storing numbers, prefer unsigned to signed because signed overflow is UB. There's an interesting caveat to this one - if you rely on sanitizers like ubsan to catch overflow errors, using unsigned won't work. It will happily and silently overflow.
- Leherenn 4y agoFor what it's worth (though you likely know it), you can add "-fsanitize=unsigned-integer-overflow" to trigger a diagnostic even if unsigned integer overflow is not UB. Of course, this only works if your code does not rely on unsigned integer overflow or if you suppress each case one by one. I would tend to say this is a good practice to enable it, because the number of times where you rely on the overflow should be very low and well documented.
- FartyMcFarter 4y agoI had forgotten about that one - that's a great solution, and I agree that in most programs unsigned overflow is rarely desirable, so explicit annotation makes sense for those cases.
- pkhuong 4y agoThis all makes sense at first sight, but remember that we can always take the difference between the address of an array's first element and one past the end, so you run into a lot of issues when an array (or any object) is larger than PTRDIFF_MAX bytes. https://trust-in-soft.com/blog/2016/05/20/objects-larger-than-ptrdiff_max-bytes/ https://trust-in-soft.com/blog/2016/05/20/objects-larger-tha...
- wnoise 4y agoPreviously: https://news.ycombinator.com/item?id=9988266 https://news.ycombinator.com/item?id=9988266 And part 2: https://news.ycombinator.com/item?id=10156265 https://news.ycombinator.com/item?id=10156265
- purpleblue 4y agoThe mathematical definition of "integer" includes the set of negative integers. That's the whole point, otherwise we could just use natural numbers. So making ints signed makes the most sense otherwise there's an impedance mismatch that you need to explain away.
- benj111 4y agoBut neither signed or unsigned ints can represent all ints. It's a question of what subset of ints you want to be able to represent.
- pornel 4y agoIntegers can also be infinitely large. C doesn't use mathematical definitions, but its own.
- mcguire 4y agoThe mathematical definition of "integer" is unbounded above and below. The "numbers" used in computing systems are not mathematical numbers in any way.
- Asooka 4y agoThe numbers used in computing systems are normal mathematical numbers in the Z_2^32 field. The signed ones are shifted by half the range in the negative direction. There is nothing un-mathematical about them, people who have studied any math in university, i.e. all CS graduates, should have gone through an introductory algebra course that covers fields, rings, etc. So I'm not sure why people keep saying ints are not mathematical.
- tsimionescu 4y agoThat depends on the computing system. You're right if we're talking about the processor level (on any modern processor), but the "signed ints" in C or C++ are not a field at all, since INT_MAX + 1 is undefined, as are -INT_MIN and INT_MIN - 1 (and many many many multiplications). Also, as someone else was pointing out, unsigned short arithmetic is not well defined either.
- cannam 4y agoI agree with the content of this article, but all the same my advice is usually the opposite - for reasons that its author sets out rather well in the table in his follow-up article (https://blog.robertelder.org/signed-or-unsigned-part-2/ https://blog.robertelder.org/signed-or-unsigned-part-2/) I wrote a little piece about this myself once (http://soundsoftware.ac.uk/c-pitfall-unsigned.html http://soundsoftware.ac.uk/c-pitfall-unsigned.html) I suppose I would loosely characterise my view as - if your profession is "C programmer" and you really know what you're doing, you probably want to use unsigneds most of the time - if your profession is something else, e.g. signal processing researcher who has to translate an algorithm to C or C++, you are probably better off not using unsigneds at all.
- ghoward 4y agoI think I agree with you, as much as I don't want to. I am a C programmer, and I think I do know what I am doing. (I implemented safe, UB-free two's-complement arithmetic in C using only unsigned integers.) I prefer unsigned. But for someone who doesn't know, as long as they're not working on anything critical, signed might be "good enough".
- dvh 4y agoI hate that stdint.h (or whoever) defines those types as uint8_t instead of simply uint8. Why was the _t necessary?
- pornel 4y agoA shorter name probably broke someone's code in 1998. _t acts as a namespace, and that's all namespacing you can get in C.
- mytailorisrich 4y agoThe convention '_t' is to unambiguously indicate that the name is a type. Some people also use '_e' for enums, etc.
- qsort 4y agoThe _t suffix is reserved for types defined by the standard. Although this is an excess of pedantry, strictly speaking you should not be defining names ending in _t.
- mytailorisrich 4y agoBy 'the standard' do you mean POSIX? Or has that been added to a C standard? Either way, in general I'd agree this is an excess of pedantry and I don't really see an issue with using _t for types if that works for you.
- qsort 4y agoI wasn't sure, I just checked. POSIX reserves everything ending in _t. C99 and later reserve identifiers starting with "int" or "uint" and ending in "_t". Source: https://en.cppreference.com/w/c/language/identifier https://en.cppreference.com/w/c/language/identifier
- Someone 4y agoFor those wondering: that phrase has to be read as “C99 and later reserve identifiers ((starting with "int" or "uint") and ending in "_t")”, not as “C99 and later reserve identifiers starting with "int" or "uint" and identifiers ending in "_t"” It’s not as if a program with a variable called interest_rate or ford_model_t would be nonconforming according to the standard.
- jasonhansel 4y ago-fwrapv
- cesarb 4y agoReposting a comment of mine from another thread (https://news.ycombinator.com/item?id=29770689 https://news.ycombinator.com/item?id=29770689): For me, the main reason to prefer unsigned integers whenever possible is: less special cases. When a signed integer is used as an array index, the value can be in one of three ranges: the <0 range, the >=0 && <size range, and the >=size range. To validate the index, you need two comparisons. When an unsigned integer is used as an array index, there are only two ranges: the <size range, and the >=size range, and you need only a single comparison. And that's not the worst situation with signed integers. From a more general point of view, there are four "classes" of signed integer: positive (>0), zero, negative (<0), and INT_MIN. Everybody tends to forget about that last one, but it's "special" in that it can break things in unexpected ways. Negating it doesn't work (you'd expect negating any number less than zero to result in a number greater than zero, but for INT_MIN that doesn't happen). Dividing it can trap (see for instance https://kqueue.org/blog/2012/12/31/idiv-dos/ https://kqueue.org/blog/2012/12/31/idiv-dos/ which has a couple of examples) or worse. With unsigned integers, there are only two "classes", zero and non-zero, and it's common to not even need special treatment for zero, reducing the whole thing to a single "class" of values.
- jcelerier 4y ago> When a signed integer is used as an array index, the value can be in one of three ranges: the <0 range, the >=0 && <size range, and the >=size range. To validate the index, you need two comparisons. When an unsigned integer is used as an array index, there are only two ranges: the <size range, and the >=size range, and you need only a single comparison. but the need for the <= 0 case does not disappear - it's a property of integers (Z) whether you want it or not, and the very second you do an arithmetic operation such as a - b, you expect this to behave like numbers: void compute(int* array, size_t len, /* any integral type */ x) { size_t res = len - x; if(res < len) printf("%d", array[res]) } this code is wrong an awful lot of time because there isn't actually "a single range" just because the integer is unsigned - you have to make sure that `len >= x` if you want a correct result so there will be comparisons there instead, but those are much trickier to get right from my experience finding bugs in such code. and if you are going to say that "this rarely happens" - here is an excerpt of a set of simple cases I could find just by grepping on the projects I have cloned where using unsigned will cause crashes: https://pastebin.com/sTgGPV9k https://pastebin.com/sTgGPV9k
- giomasce 4y agoI don't know if I am too stupid or too smart, but I use signed when some number can be negative and unsigned when it cannot. Of course I am aware of their differences other e.g. when they wrap, and act accordingly.
- kbelder 4y agoJust smart enough.
- Asooka 4y agoPersonally, I choose to just write non-standard C and turn on my compiler's option to define signed overflow as wrapping. Note that without that option, multiplication of unsigned shorts is undefined on platforms where sizeof(unsigned short) = sizeof(int)/2. That is due to the integer promotion rules. Let's say shorts are 16-bit and ints are 32-bit, which is the case on all consumer devices. Integer promotion rules lead to both unsigned short operands being first converted to ints before multiplication. If they are both equal to 0xffff, then the result is bigger than INT_MAX, and thus undefined. Which means that just multiplying two unsigned shorts is dangerous unless you cast them to unsigned ints first. Here is a godbolt link demonstrating the problem: https://gcc.godbolt.org/z/3445PEGqY https://gcc.godbolt.org/z/3445PEGqY I have never seen the benefit of leaving signed overflow undefined, so I just make it defined to avoid weird problems like that. Most people think that all unsigned arithmetic is well defined, but unfortunately that's false.
- kevin_thibedeau 4y ago> I have never seen the benefit of leaving signed overflow undefined The benefit is that architectures are free to implement it with saturating arithmetic.
- Asooka 4y agoThat's fine. Define it as saturating on those architectures. Just being defined as "this never happens" is hugely detrimental to predicting how you're program behaves.
- Quekid5 4y ago> I have never seen the benefit of leaving signed overflow undefined, IIRC it affords huge optimization opportunities, esp. around loops. (Now, whether you like that or not is a value judgment, but a lot of people do want those optimizations.)
- simonblack 4y agoFWIW, I find myself using unsigned ints more often today than I used to. Even more so, I find myself using particular sizes of unsigned ints (uint8_t, uint16_t, etc) rather than the default "unsigned int"
- virtualritz 4y agoAlso previously: https://news.ycombinator.com/item?id=29766658 https://news.ycombinator.com/item?id=29766658
- DrBurrito 4y agoThe argument against unsigned is that people abuses unsigned as a way of false safety, as a form of assert. They think the number cannot go below zero or above maxvalue. But in practice it can under- and overflow, so this is a bogus assert. I think there is a valid point in this. So, if someone does x=(0-1), signed will contain (-1) and unsigned will contain (MAXVALUE) (255 if x is char, 8 bits). The point being that (-1) is clearly wrong and invalid, and 255 is valid but wrong. The price to pay for this is one bit of reduced range (meaning, reducing the range by half). Now, all modern processors have under- and overflow checks. It is also true that nobody uses them regularly, and that they are not easily accessible in C. Therefore: use unsigned only for modulo arithmetic. For the rest, used signed numbers, as they behave like you would expect. Also, the C++ Core Guidelines: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-signed https://github.com/isocpp/CppCoreGuidelines/blob/master/CppC... https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-nonnegative https://github.com/isocpp/CppCoreGuidelines/blob/master/CppC...