What is the correct type for array indexes in C?

Michas picture Michas · Jul 4, 2010 · Viewed 19.9k times · Source

What type for array index in C99 should be used? It have to work on LP32, ILP32, ILP64, LP64, LLP64 and more. It doesn't have to be a C89 type.

I have found 5 candidates:

  • size_t
  • ptrdiff_t
  • intptr_t / uintptr_t
  • int_fast*_t / uint_fast*_t
  • int_least*_t / uint_least*_t

There is simple code to better illustrate problem. What is the best type for i and j in these two particular loops. If there is a good reason, two different types are fine too.

for (i=0; i<imax; i++) {
        do_something(a[i]);
}
/* jmin can be less than 0 */
for (j=jmin; j<jmax; j++) {
        do_something(a[j]);
}

P.S. In the first version of question I had forgotten about negative indexes.

P.P.S. I am not going to write a C99 compiler. However any answer from a compiler programmer would be very valuable for me.

Similar question:

Answer

Johannes Schaub - litb picture Johannes Schaub - litb · Jul 4, 2010

I think you should use ptrdiff_t for the following reasons

  • Indices can be negative. Therefore for a general statement, all unsigned types, including size_t, are unsuitable.
  • The type of p2 - p1 is ptrdiff_t. If i == p2 - p1, then you should be able to get p2 back by p2 == p1 + i. Notice that *(p + i) is equivalent to p[i].
  • As another indication for this "general index type", the type of the index that's used by overload resolution when the builtin operator[] (for example, on a pointer) competes against a user-provided operator[] (for example vector's) is exactly that (http://eel.is/c++draft/over.built#16): >

    For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form

    T*      operator+(T*, std::ptrdiff_t);
    T&      operator[](T*, std::ptrdiff_t);
    T*      operator-(T*, std::ptrdiff_t);
    T*      operator+(std::ptrdiff_t, T*);
    T&      operator[](std::ptrdiff_t, T*);
    

EDIT: If you have a really big array or a pointer to a really big memory portion, then my "general index type" doesn't cut it, as it then isn't guaranteed that you can subtract the first element's address from the last element's address. @Ciro's answer should be used then https://stackoverflow.com/a/31090426/34509 . Personally I try to avoid using unsigned types for their non-ability to represent negative edge cases (loop end-values when iterating backwards for example), but this is a kind of religious debate (I'm not alone in that camp, though). In cases where using an unsigned type is required, I must put my religion aside, of course.