char[] annoyance...

xs0 xs0 at xs0.com
Tue Apr 11 02:45:58 PDT 2006


Regan Heath wrote:
> On Mon, 10 Apr 2006 16:28:00 +0000 (UTC), Kevin Bealer 
> <Kevin_member at pathlink.com> wrote:
>> In article <ops7rncsol23k2f5 at nrage.netwin.co.nz>, Regan Heath says...
>>>
>>> Take this code:
>>>
>>> void main()
>>> {
>>>     //..open a file, read line for line, on each line:
>>>
>>>     for(int i = 0; i < line.length-2; i++) {
>>>         if (line[i..i+2] != "||") continue;
>>>         //..etc..
>>>     }
>>> }
>>>
>>>
>>> There is a subtle bug. On all lines with a length of 0 or 1 it will give
>>> the following error:
>>>
>>> Error: ArrayBoundsError line_length.d(6)
>>>
>>>
>>> The problem is of course the statement "i < line.length-2". 
>>> line.length is
>>> unsigned, and when you - 2 from an unsigned value.. well lets just say
>>> that it's bigger than the actual length of the line - 2.
>>>
>>>
>>> Of course there are plently of other ways to code this, perhaps using
>>> foreach, but that's not the point. The point is that this code  _can_ be
>>> written and on the surface looks fine. Not even -w (warnings) spots the
>>> signed/unsigned problem. At the very least can we get a warning for 
>>> this?
>>>
>>> Regan
>>
>> I see the "gotcha" here, and whether conversions should be done is an
>> interesting question.  But I wanted to propose a slightly different 
>> solution.
>>
>>> for(int i = 0; i+2 < line.length; i++) {
>>
>> Since you are reference "i+2" in the expression, that is really the 
>> value what you need to be in the [0..length) range.
> 
> You're probably right.. now, if only I could train my brain to think of 
> it that way round :)
> 
>> More generally, always do the arithmetic with the signed variables in 
>> cases like this if there is a possibility of wraparound.
> 
> That's good advice, however you have to realise there is a possibility 
> of wraparound, something I didn't do (or I wouldn't have had the problem 
> in the first place) :(
> 
>> But if the automatic conversions were specified to do uint->int that 
>> would also be fine with me.
> 
> But wouldn't that introduce a bug here:
>   int a = int.max-1;
>   uint b = int.max+1;
>   assert(a < b);
> 
> wouldn't b be promoted to a signed value of <some negative number>?
> 
>> One can also imagine a language with the following inequalities:
>>
>> +>  unsigned greater-than
>> +<  unsigned less than
>> ->  signed greater than
>> -<  signed less than
>> +>= unsigned greater-or-equal
>> etc
>>
>> This would not replace the current >, <, but would essentially just be 
>> shorthand for existing expressions:
>>
>> (A +> B) is the same as (cast(uint)A > cast(uint)B)
>>
>> .. and so on, except that uint, ulong, ushort, etc would be chosen as 
>> needed.
>>
>> Is this worth doing?  Maybe - it's not a big deal to me, but the 
>> signed/unsigned question does represent a common gotcha in certain 
>> expressions.
> 
> It's a good idea, but, same problem as before; you have to realise 
> length is signed and you have to realise there is a chance for 
> wraparound. A warning about the signed/unsigned comparrison is the very 
> least we should do. I would be tempted to even make it an outright error 
> requiring a cast (or one of these new operators above) to handle.
> 
> Regan

Wouldn't it be somewhat the best, if signed/unsigned comparison actually 
worked correctly in mathematical sense?

int a;
uint b;

(a<b)  becomes (a<0 || cast(uint)a < b)
(a==b) becomes (a>=0 && cast(uint)a == b)
(a>b)  becomes (a>=0 && cast(uint)a > b)

Unfortunately, that doesn't solve the original problem

if (a < b.length-2)

What would solve it is making .length signed. Even though it should 
puristically be unsigned, and losing one bit could theoretically be a 
problem (because you couldn't allocate new byte[more than 2GB], the 
reality is that a 32-bit app can't allocate more than 2GB of RAM anyway 
(in Windows at least), and for 64-bit apps, losing that one bit is 
totally insignificant, unless someone expects a computer with 9 exabytes 
of ram anytime soon :)

And the only place really needing change (and a trivial one) would be 
the array resizing code..


xs0



More information about the Digitalmars-d mailing list