I haven't viewed any Jai streams so there is some speculation in this. What is meant by "you don't care about variable size?" In this context is seems like what is desired is a integral variable but you don't wish to specify a restriction on the range of values it can have. What you want is a representation of an integral value that is the most natural on the system. "Natural" here is something that the CPU is directly supports; most likely a register. This implies that the machine code to handle the value is minimal and fast.
So, on a 64 bit machine, a signed int fits the bill. (I presume that Jonathan feels that 32 bit programs are a small minority today.)
The advantage of such a natural value are that CPU can load and update the value quickly, there are numerous instructions to manipulate it efficiently, it can be passed on stack easily, it can easily hold the expected range of values, etc. In short, you incur minimal overhead by using it.
In the following, keep in mind that the premise is "you don't care about the size of the variable" but you may care about the range of values it holds.
A disadvantage of a signed value is when you do care about the range of valid values it may have such as an index into an array. You would add "special" processing to deal with negative values. It's not illegal to use a negative value to index into an array.
| ...
int arr[32];
...
int access(int index)
{
return arr[index]; // index can be < 0
}
|
The problem here is that you would be accessing memory outside of the array, which is probably not what was intended. By the same token, if the index is larger than the size of arr then you again are accessing memory you didn't intend to.
So you add checks
| int access(int index)
{
return (index < 0 || 32 <= index)
? -1
: arr[index];
}
|
This incurs incurs overhead that you were trying to avoid and has a non-zero run-time cost. This "robs peter and pays paul" by saving on the call but paying in the function. And you have a maintenance point if the array size changes, etc. In addition, you have to deal with the -1 value.
An unsigned value can get the compiler and the type system "on your side" by detecting any unintended calls
1
2
3
4
5
6
7
8
9
10
11
12 | int access(unsigned index)
{
return (32 <= index)
? -1
: arr[index];
}
void f()
{
int a = -1;
int result = access(a); // type mismatch
}
|
This only handles half of the range problem but at least you are getting some help. If the index is calculated then the type system can play a larger role.
| int calculate_index(/*...*/)
{
int index = -1;
//...
return index;
}
void f()
{
access(calculate_index(/*...*/)); // waraning: type mismatch
}
|
The advantage of the using type system is the time savings catching these types of program errors.
OTOH, there can be a cost in conversion from unsigned to signed (unless I'm mistaken.) TANSTAAFL. And mixing signed and unsigned types in calculations have problems as well. John Lakos uses string lengths to demonstrate.
| unsigned lenght(const char * s);
char * me = "me";
char * you = "you";
int diff_you_me = length(you) - length(me); // 1
int diff_me_you = length(me) - length(you; // 4 bazillion
|
You might expect that (you-me) == -(me-you) or the reverse, but not 4 bazillion.
It really depends upon the "nature:" of the variable and it's use. If it is naturally unsigned (negative values have no meaning) then you can enlist the compiler to help you insure this but it isn't a complete guarantee.
What I would suspect Jonathan's suggestion concerns that in the majority of cases where the size of the variable is unimportant then so is the sign of it. (IIRC, he does read this forum so he can give a more definite answer.) Too much time and effort is spent bounds checking when the actual case never occurs. In the absence of a real need, a signed value is the most efficient.
YMMV
- tim