When is it useful?
On a 32 bit platform, for file offsets, maybe that last bit was useful for a few years. But as soon as disks got bigger than 4GB, you needed to switch to a 64 bit `off_t` and `lseek` anyways.
For array indexes, on a 32 bit platform, there is really only one VERY CONTRIVED case where that last bit for `unsigned` matters: your program creates a single array of bytes greater than 2GB. As soon as it's an array of 2-byte shorts or anything larger, you never need that last bit. And the configurations where a 32 bit kernel lets a program use more than 2GB of address space were not super common - it was better to switch to a 64 bit platform at that point.
On 64 bit platforms, you never need that last bit for array indexes or file offsets because no system has 10,000 petabytes of memory or 10,000 petabyte file sizes. And they won't for a while. Unless clock speeds get back on Moore's law, that for loop is going to take a long time to run anyways...
> Writing loops like this is often a code smell.
One person's code smell is another person's daily idiom. I think signed array indexes are like complex numbers - people who don't need them can't imagine why anyone else does. (https://github.com/golang/go/issues/19921)
For me, I frequently do math on the array index. Reverse loops are necessary sometimes (I can give examples), but let's ignore that for now. How would you write this simple loop with unsigned loop indices?
double scale = <non-integer value>
for (ssize_t ii = 0; ii<len; ii++) {
data[ii] = sinc(scale*(ii - len/2));
}
Note that `len/2` using integer division (truncating) is doing the right thing for both even and odd lengths. I really want that `sinc` function centered on a specific sample.If either `ii` or `len` is unsigned, C/C++ quietly does something awful here. Stuff like this makes me resent the STL for choosing size_t over ssize_t.
I think it's ugly in Rust too:
let len = data.len(); // usize because that's the way
for ii in 0..len {
data[ii] = sinc(scale*(ii as isize - len as isize / 2) as f64);
}
Thankfully the `as` operator binds pretty tightly.I don't think there are any good reasons to use unsigned integers for array indexes or sizes. It doesn't help with bound checking (it's the same assembly to check for unsigned out of bounds as it is to check for signed out of bounds or less than zero). It doesn't help with "large" arrays (except in one very contrived case). And it gets in the way when you need to do math on the indexes.