4

Indices are always nonnegative so I use size_t where ever possible. Why is it common to use signed integers then? What's the rationale behind it?

Lundin
  • 195,001
  • 40
  • 254
  • 396
AdHominem
  • 1,204
  • 3
  • 13
  • 32

3 Answers3

3

I think it's mainly due to a few reasons that work together:

  • History, I think int was basically thought of as "a machine word" back in the day (and probably still is, by many). So it's kind of "the default type", the one you use without much further thought.
  • Many loops are short, so the better precision given by unsigned types doesn't matter which makes people not think about using an unsigned type.
  • Ease of typing, int is much nicer on the hands than unsigned or (unsigned int).
  • Many people simply don't realize how much sensical an unsigned type is for iteration, or (the horror) don't care.
unwind
  • 391,730
  • 64
  • 469
  • 606
1

If you mean things like

for(int i=0; i<n; i++)

then there is no rationale. Sloppy, lazy programmers write sloppy code, simple as that.

Unless negative numbers are used, the type should indeed be some suitable unsigned type. size_t in case the iterator is used to index an array. Otherwise uint_fastn_t, where "n" should be large enough to contain all values of the iteration.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 2
    What about: `for (int i = n - 1; i >= 0; i--)` ? – chqrlie Oct 06 '16 at 14:02
  • @chqrlie: How about `(size_t i = n ; --i != SIZE_MAX`;)`? – too honest for this site Oct 06 '16 at 14:09
  • 1
    @Olaf: you must be joking! The decent solution is `for (size_t i = n; i-- > 0;)` – chqrlie Oct 06 '16 at 14:13
  • @chqrlie: No, I'm not joking. That snippet will not have `0` in the loop, which is most times required. What specifically is wrong with my snippet? – too honest for this site Oct 06 '16 at 14:21
  • @chqrlie Most often, that should be rewritten as `for (size_t i=0; i – Lundin Oct 06 '16 at 14:44
  • @Olaf: *that snippet* will have `0` in the loop. Your snippet is unintuitive. I am not even sure the standard mandates that `(size_t)0 - 1 == SIZE_MAX`. – chqrlie Oct 06 '16 at 14:46
  • 1
    @chqrlie Unsigned integer overflows are actually well-defined behavior, unlike signed integer overflow. – Lundin Oct 06 '16 at 14:47
  • @chqrlie: Please read the standard before joking on correct code. – too honest for this site Oct 06 '16 at 14:49
  • @Olaf: `(size_t i = n ; --i != SIZE_MAX;)`may be *correct* and have defined behavior by the Standard, but is not really good code. It would fail most code review standards. `(size_t i = n; i-- > 0;)` will enumerate all index values in the range `n-1`..`0`. – chqrlie Oct 06 '16 at 14:59
  • @Olaf: try it with `n = 1` and you will see how it works. Post decrementing `i` does the trick. If you get fooled by this, it is a major argument for using signed indices ;-) – chqrlie Oct 06 '16 at 15:16
  • @chqrlie: What's wrong? It will loop once with `i == 0` in the body. Exactly what it is expected to do. – too honest for this site Oct 06 '16 at 15:20
  • @Olaf: of course that's what it is expected to do, why did you write *No, that would be the range `n-1` to `1`* ? – chqrlie Oct 06 '16 at 15:21
  • @chqrlie: Now you confuse the posters? But I talked about my snippet, not yours. Anyway, this discussion is useless. I should have stopped once you started with the offence. – too honest for this site Oct 06 '16 at 15:23
  • @Olaf: what offense? What were you referring to when you commented *That snippet will not have 0 in the loop, which is most times required.*? – chqrlie Oct 06 '16 at 15:29
  • `(size_t i = n; i-- > 0;) {`, `(size_t i = n ; --i != SIZE_MAX;) {` both iterate the loop `n` times `n-1` to `0` for all `n`. `(size_t i = n; i-- > 0;) {` is more common. `(size_t i = n ; --i != SIZE_MAX;) {` would be better in select cases - would need to see overall code to assess the preferred loop coding (these 2 or others or signed vs. unsigned types) as it is context dependent. Agree strongly with "Unless negative numbers are used, the type should indeed be some suitable unsigned type. " – chux - Reinstate Monica Oct 06 '16 at 15:45
0

unsigned types have a major discontinuity at 0, it is safer for programmers to use a signed type for some loops such as running indices downwards. We often see this:

for (int i = n - 1; i >= 0; i--) {
    ...
}

Using an unsigned type in the above loop fails. There are ways to write such a loop with an unsigned type, but many programmers will find it less intuitive:

for (unsigned i = n; i-- > 0; ) {
    ...
}

Furthermore, indices are not necessarily unsigned: negative indices can be used with C pointers and some algorithms are written to take advantage of this.

The reason for the widespread use of int is historical: size_t was introduced late in the game and only became larger than unsigned quite recently, except for some older exotic systems.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    There is no "discontinuity", the behaviour is well defined: they wrap. Signed integers invoke UB on overflow and are not sufficient for indexing an array. This is more vital for I32LP64 or I32LLP64 systems like POSIX-64 or Windows-64. – too honest for this site Oct 06 '16 at 14:18
  • That would be a point if there is a discontinuity at 0, but which language does have one there? I can only speak for C which even features a type solely for that purpose (size_t). – AdHominem Oct 06 '16 at 14:21
  • Discontinuity does not mean UB, wrapping **is** a discontinuity. `ssize_t` would solve the range issue. The question was about the habit to use `int` instead of `unsigned` or `size_t`. – chqrlie Oct 06 '16 at 14:21
  • Unless you use a gray-code counter, you always have a "discontinuity" when counting (i.e. more than one bit changes). – too honest for this site Oct 06 '16 at 14:24
  • 1
    `for (size_t i=0; i – Lundin Oct 06 '16 at 14:50