Re:Pitfalls in C and C++: Unsigned Types (2011)
by cgore on Wednesday September 25, 2013 @02:46PM
(#6446474)
Actually, array indices do make sense as negative. For example, you might want an array going from a[-500] to a[700], without having to adjust the index all the time. It is much nicer if the array does it for you. Fortran lets you change the starting index if I remember correctly.
Most commonly you'll actually want to go positive, and have an array going from a[1970] to a[2013] or something similar.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by frenchy on Wednesday September 25, 2013 @02:58PM
(#6446548)
Perhaps, though from a language design standpoint I think that adds unnecessary complexity. Either way, we're talking about C here, and in that context signed integers don't make sense as array indexes.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by dllthomas on Wednesday September 25, 2013 @03:05PM
(#6446603)
The fact that we're talking about C doesn't really make a difference, if you genuinely desire negative indices:
int array[FOO];
int *shifted_array = array + FOO/2;
Re:Pitfalls in C and C++: Unsigned Types (2011)
by cgore on Wednesday September 25, 2013 @03:11PM
(#6446638)
That's actually a nice trick. I just hope the compiler doesn't complain about it. I'll have to try that sometime.
EDIT: just tried it. The following works:
#include <stdio.h>
int main(void)
{
int array[100];
int *shifted_array = array - 1980;
for (int i = 0; i < 100; i++)
array[i] = i;
for (int j = 1980; j < 2014; j++)
printf("shifted_array[%d] = %d\n", j, shifted_array[j]);
return 0;
}
Re:Pitfalls in C and C++: Unsigned Types (2011)
by dllthomas on Wednesday September 25, 2013 @03:44PM
(#6446837)
Yeah, there's a couple cases where this kind if thing cleans things up; use it with caution though.
More amusing, but less useful outside the IOCCC:
The C subscripting operator performs an basically syntactic transformation, turning a[i] into (*(a + i)). This means that, counter-intuitively (if you're thinking of [] as a lookup) the construct i[a] works too because plus is commutative.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by elbee on Wednesday September 25, 2013 @02:59PM
(#6446557)
On the other hand unsigned types are a huge pain if you want to iterate through an array backwards because you have to use subtraction. A lot of people end up with something like this:
unsigned int i = strlen(s) - 1;
for (; i >= 0; --i) { // BUGBUG
if (s[i] == '.') {
break;
}
}
(Yes, you can make it work, but it is very error-prone when people try).
Re:Pitfalls in C and C++: Unsigned Types (2011)
by pslam on Wednesday September 25, 2013 @03:08PM
(#6446617)
Simple transformation:
for (unsigned i = strlen(s); i > 0; --i) {
if (s[i - 1] == '.') break;
}
Easy to see that s[i-1] does not underflow the array due to the loop invariant i>0. It's usually easy to convert signed iteration code to unsigned and when I see this, I can tell the author spent the time to consider what happens at the limits of their inputs.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by beagle3 on Thursday September 26, 2013 @12:34AM
(#6448976)
gcc has been complaining about that one for the last 15 years or so, saying "condition is always true i >= 0".
Some errors are more subtle and not flagged by compilers, but many are.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by oleganza on Wednesday September 25, 2013 @02:46PM
(#6446470)
buf[-1] is equally incorrect as buf[4294967295], so int being signed or unsigned does not make any difference to the correctness of your loop.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by dllthomas on Wednesday September 25, 2013 @02:55PM
(#6446529)
Not so. buf[-1] will step on mapped memory that is probably something else important, and you won't find out about it until later, which is hard to debug and more likely disastrous (if there's room for disaster in the use of the software). buf[4294967295] will try to use memory that is almost certainly unmapped and unavailable, and will segfault immediately - right on the line that caused the problem - and won't take any potentially disastrous actions.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by pslam on Wednesday September 25, 2013 @02:57PM
(#6446545)
buf[4294967295] is perfectly valid and expected on platforms where size_t is 64 bits and int is 32 bits. buf[-1] is almost always a bug, and would be useful to default (with overrides) as such.
Re:Pitfalls in C and C++: Unsigned Types (2011)
by dllthomas on Wednesday September 25, 2013 @03:13PM
(#6446644)
if buf[4295967295] is valid, then you should almost certainly be indexing with something bigger than 32 bits, and the max of your index is again likely to be invalid (and, as I've mentioned elsewhere, more usefully invalid than -1).
Re:Pitfalls in C and C++: Unsigned Types (2011)
by dllthomas on Thursday September 26, 2013 @03:14AM
(#6449349)
On reflection, using something larger doesn't help, since if sizeof(void*) == sizeof(i) then a[(__typeof(i))-1] will almost certainly refer to the same address whether i is signed or unsigned.
I think most languages would benefit from unsigned types being the default, and arithmetic overflow being a hard error unless otherwise decorated. Signedness and lenient overflow promote laziness. Array indexes don't make sense as signed, yet most people prefer to iterate arrays with a signed type, e.g most commonly:
Far too many people rely on signed integer overflow working as you expect the underlying machine to handle it - but that's not what the C spec says and not how a compiler handles it either.There are countless security issues I've had to fix related to signed types in what is supposed to be secure code. These would not have occurred if the author was forced to use an unsigned type, and had to consider the extreme limits of the values it can take on. Subtle things such as the addressable limit of memory being naturally unsigned, but buffer sizes being passed as signed, can cause easy exploits, and are stupidly commonplace.