41

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?

JFMR
  • 23,265
  • 4
  • 52
  • 76
brettwhiteman
  • 4,210
  • 2
  • 29
  • 38
  • 10
    It's actually safer to use `int` if `length` may be negative. If you use `unsigned int`, then `length` gets converted to `unsigned int` too. -1 becomes something like 4294967295, and bad things happen. – Brian Bi Mar 23 '14 at 05:34
  • 3
    I agree, but when do you have a negative length? I'm more talking about cases when negatives won't be involved - such as the above. – brettwhiteman Mar 23 '14 at 05:37
  • If `length` is much smaller than `INT_MAX` then it is equivalent to use `int` and `unsigned int` . I go with the one that is less typing :) – M.M Mar 23 '14 at 05:50
  • if `length` is of `int` type why do you need to use `unsigned`? – The Mask Mar 23 '14 at 05:51
  • At the machine level, signed and unsigned integers operate with exactly the same logic. Unless your value of `i` goes to the max value (which is very unlikely if `i` is an array index and `i` is 32-bits or bigger), it doesn't really matter if you use signed or unsigned. The *only* reason `unsigned` exists is if you actually need that most significant bit to extend your positive range by another factor of 2 but not more. – Mark Lakata Mar 23 '14 at 05:52
  • Some functions return a +ve number for a good result and a -ve number for an error code. It is not possible to do this with unsigned. – cup Mar 23 '14 at 08:09
  • @cup It's fine in that case - I'm not condemning all uses of signed integers! Only cases where an unsigned integer could be used. – brettwhiteman Mar 23 '14 at 08:38

6 Answers6

37

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.

Paul Hankin
  • 54,811
  • 11
  • 92
  • 118
  • 3
    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. – Stefano Buora Jul 12 '17 at 19:37
  • 3
    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 Nov 20 '18 at 07:56
  • 1
    @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 Nov 20 '18 at 07:57
19

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).

user3344003
  • 20,574
  • 3
  • 26
  • 62
  • 7
    I find some of the responses here worrisome. Using int because it will cover up a programming error is scary. – user3344003 Mar 25 '14 at 05:17
  • 4
    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. – nemetroid May 07 '18 at 16:12
  • 1
    @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 Nov 20 '18 at 07:55
  • 4
    @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. – nemetroid Nov 21 '18 at 10:25
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.

Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
  • 3
    You can reverse the loop with unsigned int too: `for (unsigned int i = length; i--; )` – M.M Mar 23 '14 at 05:50
  • @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. – Mark Lakata Mar 23 '14 at 05:56
  • 2
    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. – Benjamin Lindley Mar 23 '14 at 05:59
  • Not *weird things* but *integral promotion*. – Ben Jackson Mar 23 '14 at 06:10
  • @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). – Aconcagua Nov 20 '18 at 07:58
4

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.

sherrellbc
  • 4,650
  • 9
  • 48
  • 77
1

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.

PyNEwbie
  • 4,882
  • 4
  • 38
  • 86
Khoi Pham
  • 27
  • 3
  • 2
    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). – brettwhiteman Mar 23 '14 at 05:41
  • 3
    @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. – Benjamin Lindley Mar 23 '14 at 05:44
  • 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 Nov 20 '18 at 07:50
0

this tiny code is usecase related, if you call some vector element then the prototype is int but there're much modern ways to do it in c++ eg. for(const auto &v : vec) {} or iterators, in some calculcation if there's no substracting/reaching a negative number you can and should use unsigned (explains better the range of values expected), sometimes as many posted examples here shows you actually need int but the truth is it's all about usecase and situation, no one strict rule apply to all usecases and it would be kinda dumb to force one over...

LiorA
  • 441
  • 3
  • 13