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:
- size_t vs. uintptr_t
The context of this question if different though.
Since the type of
sizeof(array)
(andmalloc
's argument) issize_t
, and the array can't hold more elements than its size, it follows thatsize_t
can be used for the array's index.EDIT This analysis is for 0-based arrays, which is the common case.
ptrdiff_t
will work in any case, but it's a little strange for an index variable to have a pointer-difference type.My choice: ptrdiff_t
Many have voted for
ptrdiff_t
, but some have said that it is strange to index using a pointer difference type. To me, it makes perfect sense: the array index is the difference from the origin pointer.Some have also said that
size_t
is right because that is designed to hold the size. However, as some have commented: this is the size in bytes, and so can generally hold values several times greater than the maximum possible array index.I almost always use
size_t
for array indices/loop counters. Sure there are some special instances where you may want signed offsets, but in general using a signed type has a lot of problems:The biggest risk is that if you're passed a huge size/offset by a caller treating things as unsigned (or if you read it from a wrongly-trusted file), you may interpret it as a negative number and fail to catch that it's out of bounds. For instance
if (offset<size) array[offset]=foo; else error();
will write somewhere it shouldn't.Another problem is the possibility of undefined behavior with signed integer overflow. Whether you use unsigned or signed arithmetic, there are overflow issues to be aware of and check for, but personally I find the unsigned behavior a lot easier to deal with.
Yet another reason to use unsigned arithmetic (in general) - sometimes I'm using indices as offsets into a bit array and I want to use %8 and /8 or %32 and /32. With signed types, these will be actual division operations. With unsigned, the expected bitwise-and/bitshift operations can be generated.
If you start at
0
, use size_t because that type must be able to index any array:sizeof
returns it, so it is not valid for an array to have more thansize_t
elementsmalloc
takes it as argument, as mentioned by AmnonIf you start below zero, then shift to start at zero, and use
size_t
, which is guaranteed to work because of the reasons above. So replace:with:
Why not to use:
ptrdiff_t: the maximum value this represents may be smaller than the maximum value of
size_t
.This is mentioned at cppref, and the possibility of undefined behavior if the array is too large is suggested at C99 6.5.5/9:
Out of curiosity,
intptr_t
might also be larger thansize_t
on a segmented memory architecture: https://stackoverflow.com/a/1464194/895245GCC also imposes further limits on the maximum size of static array objects: What is the maximum size of an array in C?
uintptr_t: I'm not sure. So I'd just use
size_t
because I'm more sure :-)I use
unsigned int
. (though I prefer the shorthandunsigned
)In C99,
unsigned int
is guaranteed to be able to index any portable array. Only arrays of 65'535 bytes or smaller are guaranteed to be supported, and the maximumunsigned int
value is at least 65'535.From §the public WG14 N1256 draft of the C99 standard:
In ANSI C, the maximum portable array size is actually only 32'767 bytes, so even a signed
int
will do, which has a maximum value of at least 32'767 (Appendix A.4).From §2.2.4 of a C89 draft:
In your situation, I would use
ptrdiff_t
. It's not just that indicies can be negative. You might want to count down to zero, in which case signed types yield a nasty, subtle bug:That won't happen if you use
ptrdiff_t
or any other suitable signed type. On POSIX systems, you can usessize_t
.Personally, I often just use
int
, even though it is arguably not the Correct Thing To Do.