-6

Are there any examples of legitimately good use of an unsigned data (i.e. unsigned int) or should use of unsigned data types just be considered very bad coding practice as a relic of resource impaired platforms from the 1970s and 1980s?

Consider this:

int main ()
{
    unsigned int a = 5;   /*or uint32_t if you prefer*/
    unsigned int b = 8
    unsigned int c = a - b; // I can't even store a subtraction result
                            // from my own data type!

    float f;    // ISO didn't even bother to make a signed version of float.
    double d;   // ISO didn't even bother to make a signed version of double.

    // size_t is an unsigned integer, length varies
    // (4 bytes on 32 bit platforms typically, 8 on 64 bit, ...)
    size_t size1 = 100; 
    size_t size2 = 200;

    // What's a ssize_t -- it's a signed size_t because size_t can't store subtractions.
    // So ssize_t is an bad idea to correct for the bad idea of a size_t being unsigned
    ssize_t size3 = size1 - size3;

    // unsigned operations don't overflow/underflow
    size_t  size4 = size1 - size3; // I don't even underflow, I just wrap.
                                    // Which means unsigned isn't even good
                                    // For use as a pseudo data "validation"
}

Additionally, the C definition of memset, as an example:

void * memset ( void * ptr, int value, size_t num );

memset's value argument is really an unsigned char, but a great number of functions convert unsigned char to int simply to dodge use of an unsigned data type or how printf promotes char to int.

And in a scenario where unsigned is being used for the slightly greater range (i.e. 32-bit 4GB) that is more a sign that the wrong datatype is being used and that either a int64 variant or a double should actually be used to store the value to begin with.

There has to be some legitimate use of unsigned but I can't think of a scenario. So what scenario should unsigned types be used?

B. Nadolson
  • 2,988
  • 2
  • 20
  • 27
  • 5
    Integers won't magically help you un-make bad coding decisions. – this Jul 17 '15 at 15:00
  • 1
    Type `size_t` is typically unsigned, and you want to make sure that everything you use related to sizes is also unsigned, otherwise you get warnings. – Steve Summit Jul 17 '15 at 15:01
  • `float` and `double` can only be signed. `unsigned` _does have_ uses and listing all of them isn't really useful and allowed on this site, too. – edmz Jul 17 '15 at 15:11
  • 3
    Why would you imagine that `unsigned integers` are simply a relic of the past? – David Hoelzer Jul 17 '15 at 15:42
  • @David -- if I have the availability of a 64-bit signed integer, can you name a single non-novel situation that I should use a 64-bit unsigned integer. I can think of none. Don't get me wrong, I used to accept the idea that unsigned types could have uses, but the reality is you can't even subtract them. All situations --- aside from novel ones that take advantage of wrapping as a cheat --- that would benefit from use of unsigned types are memory address mapping (i.e. pointers) and with 64-bit integer even that isn't necessary. And performing an unsigned subtraction requires casting too. – B. Nadolson Jul 18 '15 at 23:02
  • How can a question asking for an any example of good coding practice (i.e. a "technical reason") that uses unsigned types be "opinion based"? I understand the -7 downvote just fine because that is just what people do when a frustrating question gets asked that they don't really know how to answer. However, the "On Hold" is by moderators is more of a mystery. If you want to be lazy why not be honest too and put my question on hold for being unpopular. Java and several newer languages have no support for unsigned data types, seems likely the engineers designing Java had technical reasons. – B. Nadolson Jul 18 '15 at 23:51
  • @black - "unsigned does have uses and listing all of them isn't really useful and allowed on this site, too." --- Then just list one. – B. Nadolson Jul 18 '15 at 23:55
  • @B.Nadolson Yes. You use the proper type for the data. For instance, a network stack handling an unsigned 64 bit integer by RFC definition. A video file using a 64 bit length field which would, again, be unsigned by definition. A file system driver permitting files with a 64 bit length which, once again, is unsigned by definition. – David Hoelzer Jul 19 '15 at 00:28
  • @B.Nadolson Bit masks and bit shifting. – edmz Jul 19 '15 at 09:21
  • @Black -- that's not true at all. bit masks and bit shifting don't have anything to do with signed or unsigned types. int a = 1 << 31, b = 0xffffffff, c = (a & b) ? 1 : 0; Regardless of signed or unsigned int, c will have the same results. Bits aren't related to sign. – B. Nadolson Jul 21 '15 at 04:41
  • @David -- a video file using a 64 bit length field? Do you know how big any integer that needs 64 bits is instead of 63 bits (the signed version)? It's a massive number --- all the videos that have ever been made don't have enough bits to require that length. – B. Nadolson Jul 21 '15 at 04:53
  • @B.Nadolson Your dogged belief that they are unnecessary has no impact on reality and good coding practice. – David Hoelzer Jul 21 '15 at 08:36
  • @B.Nadolson You cannot say it isn't true unless you narrow the field. In my experience, you don't want want that signed bit and don't want to take of it thereby. It can mess up things including but not limited to bit shifts and bit masks. (you'll see lots of trailing `u`/`ul`/`ull`). – edmz Jul 21 '15 at 09:23
  • @black -- ok! There is finally a good example of a meaningful behavior difference --- left shifting because that behavior is undefined if signed. It raises an interesting question in that C99 enums are int. AFAIK. – B. Nadolson Jul 21 '15 at 14:43

3 Answers3

8

The rule of the thumb in this case is very simple: use unsigned types to represent unsigned values, use signed type to represent signed values. So, in reality it is just the opposite: most of the time gratuitous use of signed types is a terrible coding practice. I'd even go as far as to say that most of integer types in the code are supposed to be unsigned. Of course, the actual ratio will depend on the application area, but for combinatorial problems and related domains: it is unsigned, unsigned and only unsigned.

Your above example with wrap-around behavior simply demonstrates typical newbie coding error. And in its essence it is no different from the popular

double d = 1/2;

followed by something like "why my d is not 0.5?".

Note also that in the domain of integral calculations unsigned types are typically more efficient than signed ones (C rounding rules for division are different from the typical machine-supported ones and make a negative impact on the performance of signed types). In mixed integer-floating point calculations signed integer types might have an edge (FPU instruction set typically supports signed integers directly, but not unsigned ones).

great number of functions convert unsigned char to int simply to dodge use of an unsigned data type

Nope. Conversion to int is a rudiment of that bygone era when C language had no function prototypes. All functions were declared without prototypes (or left undeclared), which triggered automatic promotions of smaller integer arguments to int. Once prototypes for standard functions appeared, there were intentionally tailored to be compatible with legacy behavior. For this reason you will never see a "classic" standard library function that accepts [signed/unsigned] char, [signed/unsigned] short arguments (or float for that matter). Signedness has nothing to do with it.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Your example isn't valid, part of knowing C in a Introduction To C 101 class is that 1 is an integer and that 1.0 is a double. What does that have to do with legitimate uses of unsigned types? – B. Nadolson Jul 18 '15 at 23:08
  • @B. Nadolson: The example is not about `1` being an integer and `1.0` being a double. (You can rewrite that example as `double d = a / b` if you wish, without any `1` in it). The example is about the simple fact that in C language the type of the recipient variable does not affect how the expression is evaluated. `1 / 2` is integer division regardless of what we we store the result in. The same applies to subtractions you used in your OP. – AnT stands with Russia Jul 19 '15 at 00:26
  • Most of the comments in your code make no sense whatsoever, so it is hard to figure out what you expected would happen in `unsigned int c = a - b` case and why you made your "I can't even store.." statement. But your `ssize_t size3 = size1 - size3;` example (BTW, did you mean `size2` in subtraction) looks like the same mistake as `double d = 1 / 2`. So, yes, my example is 100% valid. – AnT stands with Russia Jul 19 '15 at 00:27
  • It's a typo, yes. And what I meant by "can't even store" is unsigned x = (unsigned)100 - unsigned(200) is some very large number. The main feature of unsigned types is you can't reliably subtract them and they offer no other benefit, except situations that really, really ancient processors would need. – B. Nadolson Jul 21 '15 at 04:47
  • @B. Nadolson: I don't understand what you mean by "can't reliably subtract". Arithmetic behavior of unsigned integers is strictly defined by the language specification. In C language it does not get more *reliable* than that. And yes, you can reliably subtract them. When one is working with non-negative quantities, there's rarely a need to subtract a greater value from a smaller value. And when such need arises, it usually happens under rather extraordinary circumstances, which do warrant extra caution. – AnT stands with Russia Jul 21 '15 at 04:53
  • Again, claiming that unsigned subtraction is "unreliable" is no different from claiming that integer division is unreliable since it always produces "truncated" result. Dealing with such "unreliabilities" in C language is just something that has to be learned and kept in mind. – AnT stands with Russia Jul 21 '15 at 04:54
  • You are missing the point, this particular feature is a tool for situations which almost entirely no longer exist except for char. There aren't any real world situations where you need a unsigned int 64 because an signed int 64 doesn't have the storage capacity (i.e. 16-bit computing has been dead for decades) and even in the 32-bit era int64 has been available for large memory spaces and was the correct solution. The use of unsigned int types is legacy behavior propogated only out of habit and by people who mistakenly think unsigned int performs some sort of data validation (it doesn't). – B. Nadolson Jul 21 '15 at 06:51
  • @B. Nadolson: No, no, no. You seem to be trapped in some weird belief that use of unsigned types was somehow tied to getting a greater range out of them. This has nothing to do with reality. 1 extra bits solves nothing in terms of range. In reality it is the other way around: there are very few specific real-world situations were you would need *signed* types - situations where you have to deal with negative values. However, most of computer programming is *combinatorial* in nature. And combinatorics is dominated by non-negative quantities, which is why unsigned types dominate. – AnT stands with Russia Jul 21 '15 at 07:11
  • I work in computational geometry mostly. And I use signed integers to represent coordinates and coordinate-derived values, since coordinates can naturally be negative. But that would be the only thing I'd use signed integer types for. Everything else is unsigned, unsigned and only unsigned. It is not a matter of extending range. It is a matter of common sense: use unsigned types to represent unsigned values (same for signed). – AnT stands with Russia Jul 21 '15 at 07:14
  • @ ant - I imagine most variables are never intended to have negative values. So how are you bounds checking these unsigned types to make sure they never "go below zero" since unsigned types don't go below (zero and just wrap around) nor do they cause an overflow/underflow signal? As an example an array index is generally never meant to go below zero, but it is quite common to bounds check them. – B. Nadolson Jul 21 '15 at 14:25
  • I'm done and I appreciate the discussion. Before I posted this, I wasn't aware Java *doesn't have unsigned types* --- and this discovery essentially locks it up. I understand that some use the unsigned type for human documentation purposes, but that doesn't use C language features and has almost the same effect as typedef float positive_number or float positive_range, it's just a name. I was trying to get into the technical differences between signed and int behavior by the C language, and see if there were any technical uses for unsigned and so far no one has mentioned one. – B. Nadolson Jul 21 '15 at 14:35
  • @B. Nadolson: Bounds checking unsigned types in subtraction situations is performed *before* performing the subtraction, not *after*. I.e. you never subtract greater value from smaller value (unless you are deliberately looking for modulo wrap-around). Bounds checking arrays is actually *simpler* with unsigned types specifically because of their well-defined wrap-around behavior: all you need to check is that the index is *smaller* than array size. Overflow and wrap-around on subtraction will make the index *greater* than array size and the above check will catch that situation. See? Easy. – AnT stands with Russia Jul 21 '15 at 14:44
  • You remark about Java is irrelevant. Your remark about not seeing any "technical uses" for unsigned types is amusing. So far you failed to demonstrate any technical uses for signed types either, besides stubborn and completely unfounded assertions that they are somehow "better". May I remind you that the Standard Library of C++ language makes massive use of unsigned types in full accordance with the principle I mentioned above: use unsigned types to represent inherently non-negative values. – AnT stands with Russia Jul 21 '15 at 14:46
  • @ Ant - interesting insight into unsigned bounds checking there. Still if you are checking before subtraction, it isn't saving any time. I understand the nature of the design of the code base you are working with. Java is very relevant indirectly --- C and C++ have to carry their baggage because no one will accept old code that doesn't compile which is absolutely unacceptable and this is one of the best features of C. But Keep in mind both Microsoft and Google have copied Java --- .NET is a Java clone and Google wholesale cloned Java. – B. Nadolson Jul 22 '15 at 18:51
  • In a language made today with no need to keep quaint habits like unsigned, the use of unsigned would be very limited or non-existent. Case in point, array indexes in Swift are signed integer --- not unsigned (something I just discovered now). For some of the reasons I've pointed out. But we'll have to agree to disagree. – B. Nadolson Jul 22 '15 at 18:52
  • @B.Nadolson: Efficient hashing and checksum computations often require types which wrap cleanly mod 65536 or 4294967296. All implementations of Java have signed types that do so, but many implementations of C do not--**even when run on commonplace hardware with silent wraparound semantics**. By contrast, unsigned types which are too large to fit in a signed "int" are guaranteed to have wrapping semantics. – supercat Sep 26 '16 at 17:00
1

All types clarify and enforce purpose, and thus these are useful too. Unsigned is good programming practice to indicate the intention, to yourself and others, of use of the data element - just in the same way all types are used.

For example, a normal array index variable should never be negative and so declaring the variable unsigned should be best practice. Another usage is common in the lower sized elements of "short int" and "int", at the very least, where one may find use doubling the maximum value store-able by going unsigned (gaining another bit in the positive numbering space), rather than doubling the amount of space used. This is true despite more available storage space for "modern guys", considering one might store lots of these items, for example in a large array. Efficient/careful coding should not be relegated to the last century just because of resource enhancements.

I'd venture that abnormal lack of usage is more likely careless coding, rather than obsolescence. As for ISO standard indicating obsolescence, the ISO specs I find they specify unsigned for all integer types: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf, in section 6.2.5 "There are five standard signed integer types, designated as signed char, short int, int, long int, and long long int" and then goes on to say "For each of the signed integer types, there is a corresponding (but different) unsigned integer type". As for floats, which are quite a different type, the argument is made at Why no unsigned floating point types? that at 32+ bits, and with normal usage, the feature was probably not found useful enough - and its always been that way, not related to changes over time.

Community
  • 1
  • 1
Cris Mooney
  • 185
  • 10
  • Unsigned integer does nothing to stop a bad array index. Unsigned integer doesn't overflow and doesn't underflow, it just wraps and is expected to wrap. Therefore unsigned int x = -1 will compile with no warnings and it is part of the C specification that it won't have a warning as I understand it. So while you are advocating it for "type checking" or "validation", it doesn't do either of those which is part of my point. In fact, part of my argument might even be that individuals like yourself will mistakenly believe it helps with validation. – B. Nadolson Jul 18 '15 at 23:12
  • @B. Nadolson: Firstly, what "warning" you are talking about? In 99.9% real-life cases array indexing is done by a run-time value, which means that compiler warnings for negative indices are impossible regardless of what kind of index you are using (not even mentioning that negative indexing is perfectly valid in C). – AnT stands with Russia Jul 21 '15 at 15:00
  • @B. Nadolson: Secondly, array indexing is checked by run-timer means, like assertions, for one example. When you use signed types, the array bounds check typically looks like `assert(index >= 0 && i < N)`. With unsigned index that check becomes simply `assert(i < N)`, which *will also catch* the situations when the index suffered negative overflow and wrap-around. You didn't know that, huh? Now you do. Anyway, the statement made by the above poster about unsigned types helping with validation is 100% correct. – AnT stands with Russia Jul 21 '15 at 15:02
  • Thank you AnT for helping to clarify some of my intentions, as well as B. Nadolson for pointing out "clarify and enforce purpose" is vauge. That aspect of my point was intentionally vague and theoretical, matching the vague theoretical inquiry, and was meant to apply not just to compilers, or even lint type extended validation tools, but also to people and any other form of code review and/or library type use of code. Lack of warning or errors by any of these does not offset the value of having the info available for documentation and validation, and seems relevant response to the inquiry. – Cris Mooney Jul 21 '15 at 16:07
  • I'd also like to note as a newbie here that I appreciate you not having down-voting my response based on your wothy considerations, and I'd like to confirm my recognition that all this ambuguity is why this is not considered a good StackOverflow question and perhaps I should have declined to participate because of the type of forum this is. – Cris Mooney Jul 21 '15 at 16:10
0

Resource restricted platforms still exist, e.g. 8-bit microcontrollers.

unsigned char i;
for (i = 0; i < 131; i++) {
    /* use i */
}

When the CPU directly supports only 8-bit operations but your loop limit is 131, you can use the 8-bit unsigned type and have your code is still efficient (i.e. not using software emulated 16-bit operations).


Update about integer promotion: Compilers are fortunately smart enough to avoid producing unnecessary code. The example above compiles to a code that really works only with 8-bit values on such 8-bit microcontrollers.

dlask
  • 8,776
  • 1
  • 26
  • 30
  • Don't default integer promotions kick in in this comparison: `i < 131`, making both types `int`, and therefore less efficient code? – this Jul 17 '15 at 15:09
  • @EugeneSh. Integer promotions aren't performed by default when comparison operator is used. I checked C11 Standard. – this Jul 17 '15 at 15:18
  • @this: Firstly, integer promotions *are* performed for relational comparisons. Formally *usual arithmetic conversions* are performed with include the promotions. Secondly, such promotions are only *language concepts* which describe the formal semantics of the operation. They do not in any way imply that the compiler has to *literally* perform them in the actual code. All the compiler has to do is generate code that produces the correct result. – AnT stands with Russia Jul 17 '15 at 15:23
  • @AnT But they arent performed for comparisons: *The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.* Also *6.5.8 Relational operators* doesn't mention promotions at all, while arithmetic chapters explicitly does. – this Jul 17 '15 at 15:23
  • @this: C11, 6.5.8/3: *If both of the operands have arithmetic type, the usual arithmetic conversions are performed.* You quoted a footnote, which is non-normative and is not supposed to be complete apparently. – AnT stands with Russia Jul 17 '15 at 15:24
  • @AnT What can I say. The footnote 58 disagrees with you statement. I'm sorry but you are wrong. If both types are the same and used with a comparison operator then no promotions are applied. – this Jul 17 '15 at 15:26
  • 1
    @this: A footnoote can't "disagree" with anything. It is a footnote and footnotes are non-normative. Meanwhile, I quoted the actual normative text. I'm tempted to say that it "trumps" your footnote, but there's no need for that. That footnote simply doesn't say what you think it says. – AnT stands with Russia Jul 17 '15 at 15:28
  • @AnT I'm sorry but you are wrong. If both types are the *same* and used with a comparison operator then no promotions are performed. *6.5.7 Bitwise shift operators* and *6.5.3.3 Unary arithmetic operators* clearly state that integer promotions are applied on each operand, while *6.5.8 Relational operators* don't. – this Jul 17 '15 at 15:28
  • @AnT That is true. My comments don't apply for this answer. But I was arguing the point where they are the same. If you compare two integers with the type `unsigned char` using a comparison they don't get promoted. ( And please don't use ad-hominem attacks ) – this Jul 17 '15 at 15:32
  • 1
    @this: Wrong. Even when you compare two `unsigned char`s, they get promoted. C11 6.5.8/3 explicitly says so. Usual arithmetic conversions always promote their operands, unconditionally. Where you got the idea that "identical types disable promotions" is not clear to me. There's nothing like that in C standard. – AnT stands with Russia Jul 17 '15 at 15:33
  • @AnT Actually the rule you quote is from the chapter: *6.3.1 Arithmetic operands*. Relational operands are not covered by that rule. – this Jul 17 '15 at 15:36
  • @AnT Oh, I see. I though you were still in 6.3.1 I just found that *6.5.8 Relational operators* states *If both of the operands have arithmetic type, the usual arithmetic conversions are performed.* Which seems to be the part you quoted. So they the promotions do get applied. Too bad you had to resort to ad-homined over a misunderstanding, and trying to communicate using a sub-par channel. ( See I didn't notice that you were quoting Relational chapter because you only used the numbers not the full title. Oh well...) – this Jul 17 '15 at 15:43
  • @this: Unsigned types will only be promoted to signed types in cases where all values of the original type will fit in the new one. Comparison or logical operators applied to two values of the same small unsigned type will yield the same result as they would if applied to the original types. Differences arise with arithmetic and left-shifting operators. – supercat Sep 26 '16 at 17:12