C/C++ use of int or unsigned int

25,515

Solution 1

Using unsigned can introduce programming errors that are hard to spot, and it's usually better to use signed int just to avoid them. One example would be when you decide to iterate backwards rather than forwards and write this:

for (unsigned i = 5; i >= 0; i--) {
    printf("%d\n", i);
}

Another would be if you do some math inside the loop:

for (unsigned i = 0; i < 10; i++) {
    for (unsigned j = 0; j < 10; j++) {
        if (i - j >= 4) printf("%d %d\n", i, j);
    }
}

Using unsigned introduces the potential for these sorts of bugs, and there's not really any upside.

Solution 2

It's generally laziness or lack of understanding.

I aways use unsigned int when the value should not be negative. That also serves the documentation purpose of specifying what the correct values should be.

IMHO, the assertion that it is safer to use "int" than "unsigned int" is simply wrong and a bad programming practice.

If you have used Ada or Pascal you'd be accustomed to using the even safer practice of specifying specific ranges for values (e.g., an integer that can only be 1, 2, 3, 4, 5).

Solution 3

I chose to be as explicit as possible while programming. That is, if I intend to use a variable whose value is always positive, then unsigned is used. Many here mention "hard to spot bugs" but few give examples. Consider the following advocate example for using unsigned, unlike most posts here:

enum num_things {
    THINGA = 0,
    THINGB,
    THINGC,
    NUM_THINGS
};

int unsafe_function(int thing_ID){
    if(thing_ID >= NUM_THINGS)
        return -1;

    ...
}

int safe_function(unsigned int thing_ID){
    if(thing_ID >= NUM_THINGS)
        return -1;

    ...
}

int other_safe_function(int thing_ID){
    if((thing_ID >=0 ) && (thing_ID >= NUM_THINGS))
        return -1;

    ...
}

/* Error not caught */
unsafe_function(-1);

/* Error is caught */
safe_function((unsigned int)-1);

In the above example, what happens if a negative value is passed in as thing_ID? In the first case, you'll find that the negative value is not greater than or equal to NUM_THINGS, and so the function will continue executing.

In the second case, you'll actually catch this at run-time because the signedness of thing_ID forces the conditional to execute an unsigned comparison.

Of course, you could do something like other_safe_function, but this seems more of a kludge to use signed integers rather than being more explicit and using unsigned to begin with.

Solution 4

If length is also int, then you should use the same integer type, otherwise weird things happen when you mix signed and unsigned types in a comparison statement. Most compilers will give you a warning.

You could go on to ask, why should length be signed? Well, that's probably historical.

Also, if you decide to reverse the loop, ie

for(int i=length-1;i>=0 ;i--)
{
   // do stuff
}

the logic breaks if you use unsigned ints.

Solution 5

I think the most important reason is if you choose unsigned int, you can get some logical errors. In fact, you often do not need the range of unsigned int, using int is safer.

Share:
25,515
developerbmw
Author by

developerbmw

Updated on December 04, 2020

Comments

  • developerbmw
    developerbmw over 3 years

    In a lot of code examples, source code, libraries etc. I see the use of int when as far as I can see, an unsigned int would make much more sense.

    One place I see this a lot is in for loops. See below example:

    for(int i = 0; i < length; i++)
    {
        // Do Stuff
    }
    

    Why on earth would you use an int rather than an unsigned int? Is it just laziness - people can't be bothered with typing unsigned?

  • developerbmw
    developerbmw about 10 years
    I disagree. If used properly, there should be no "logic errors." When I program, I like to be explicit, rather than use the safe option which could possibly have unintended consequences later (such as an overflow).
  • Benjamin Lindley
    Benjamin Lindley about 10 years
    @Brett: Adding one extra bit does not do much to curb the possibility of an overflow. If you're dealing with numbers large enough where overflow may be a possibility, then you should use a larger integer type such that overflow is not a possibility, resorting to an unbounded integer type if necessary.
  • M.M
    M.M about 10 years
    You can reverse the loop with unsigned int too: for (unsigned int i = length; i--; )
  • Mark Lakata
    Mark Lakata about 10 years
    @MattMcNabb - you're right! huh, many many years programming and I've never seen that pattern (where the for conditional has a side effect). Learn something new every day.
  • Benjamin Lindley
    Benjamin Lindley about 10 years
    Of course you can write a reverse-loop using unsigned ints. The point is (at least I assumed) that writing the loop is much easier to get wrong if you use unsigned.
  • Ben Jackson
    Ben Jackson about 10 years
    Not weird things but integral promotion.
  • user3344003
    user3344003 about 10 years
    I find some of the responses here worrisome. Using int because it will cover up a programming error is scary.
  • Stefano Buora
    Stefano Buora almost 7 years
    It's quite a decade that i >= 0 is reported as warning by compiler if an unsigned int is used as type for i. Even, following your example, if the '5' comes as a size of an array, usually is of size_t type (unsigned), and in order to do not have warning (or a cast) an unsigned int and a forward loop may be required.
  • nemetroid
    nemetroid about 6 years
    The "programming errors" in the other answers are only programming errors if you use unsigned integers, and perfectly sensible code if you use signed integers.
  • Aconcagua
    Aconcagua over 5 years
    There are situations where overflow is even desired, e. g. a time counter on a micro controller. What should otherwise happen if the MCU runs long enough to exceed the range of the counter? Signed integer would produce negative values on overflow - apart from that this actually is undefined behaviour up until C++20!
  • Aconcagua
    Aconcagua over 5 years
    @nemetroid The programming errors are only errors because the programmer chose the bad tool for the task he intended, or did not use it properly. It's not the fault of unsigned int.
  • Aconcagua
    Aconcagua over 5 years
    In my eyes these are just some examples of how people do now know how to correctly use the tools they chose (or for bad choice itself)... Both examples are not the fault of unsigned int, but of the programmer wanting to produce negative values with a tool that cannot. Try to tighten a nut with a screw driver, you'll likely fail. Still a screw driver is a useful tool - or would you deny?
  • Aconcagua
    Aconcagua over 5 years
    @StefanoBuora Actually, there are ways to correctly deal with unsigned int even in backward loops: for(unsigned int i = start + 1; i-- > 0; ) or for(unsigned int i = start; i <= start; --i) - the latter explicitly profiting from underflow.
  • Aconcagua
    Aconcagua over 5 years
    @MarkLakata Another pattern you might like: for(unsigned int i = length - 1; i < length; --i) (I personally don't favour one over the other, just for completeness).
  • nemetroid
    nemetroid over 5 years
    @Aconcagua: it's a truism that bugs are due to incorrectly written code. You could make the same argument about goto or null pointers or any other error-prone programming construct/tool.