10

So I ran into a huge issue at work because I had something like this in my code:

    int foo = -1;
    NSArray *bar = [[NSArray alloc] initWithObjects:@"1",@"2",@"3", nil];
    if (foo > [bar count]){
        NSLog(@"Wow, that's messed up.");
    } else {
        NSLog(@"Rock on!");
    }

As you probably already know by me posting this, the output is:

"Wow, that's messed up."

From what I gather, objective C is converting my negative number to a "signed" int and thus, killing my compare.

I saw other posts about this and they all stated what the problem was but none of them suggested any simple solutions to get this comparison to actually work. Also, I'm shocked that there are no compiler warnings, as these are causing serious issues for me.

William T.
  • 12,831
  • 4
  • 56
  • 53
  • 8
    There is a compiler warning for this but it is not on by default. Go to the Build Settings tab for your target and search for "Sign Comparison". Turn on that compiler warning. This may result in a lot of warnings. – rmaddy Apr 23 '13 at 04:51
  • 2
    You may want to reconsider what your code is meant to do, since as `count` is unsigned your if clause is basically a dead branch and could be removed. – axiixc Apr 23 '13 at 05:02
  • I'm iterating through an array by pressing "next" or "prev" buttons that change an index variable. The array values need to loop, so when the index is less than 0, it should be set to the last element in the array. If the index is greater than the array length, it should be set to 0. So there could possibly be a situation where the index is -1 and I'm comparing it to the array length as shown above. – William T. Apr 23 '13 at 05:42
  • 2
    @WilliamT. If the array index is 0 and the user hits the *previous* button, why not set the index to the max element at that point? Similarly, if the index is the max and the user hits the *next* button, set the index to 0. That way the index always refers to a valid element, and at the same time you avoid this problem. Alternately, use modular arithmetic to determine the index: `(counter % array.count)`. `previous` and `next` decrement and increment `counter`, but the actual value of `counter` doesn't matter as long as you avoid overflow and underflow. – Caleb Apr 23 '13 at 14:43
  • @Caleb The array can be changed from 4 different actions, so I simply ++ or -- the index and then call a shared function to evaluate the display but in hindsight, it may be best to check this up front on the taps. Thanks for the input. +1 – William T. Apr 23 '13 at 16:12

2 Answers2

11

The problem

The problem you're experiencing is that because foo is a signed integer and -[NSArray count] returns an unsigned integer, foo is undergoing implicit type conversion to unsigned integer. See Implicit Type Conversion for more information. Also, there's more information about type conversion rules in C here.

The solution

-[NSArray count] returns an unsigned value because an array can never have a negative number of elements; the smallest possible value is 0. Comparing an array's count to -1 doesn't make a lot of sense -- the count will always be larger than any negative number (sign problems notwithstanding).

So, the right solution here, and the way to avoid these kinds of problems, is to use a type that matches the return value of -[NSArray count], namely NSUInteger (the U is for unsigned).

Community
  • 1
  • 1
Caleb
  • 124,013
  • 19
  • 183
  • 272
  • 2
    Exactly, **when you compare make sure both operands are of exact same type**. This is the answer – Krishnabhadra Apr 23 '13 at 05:15
  • 1
    No, that's crazy. As @Caleb mentions, comparing the length of an array to `-1` doesn't *mean* anything. Use `0`. – Carl Norum Apr 23 '13 at 05:17
  • @Bhargavi What Carl said. NSUInteger *cannot* store -1, because -1 is a negative number. NSUInteger can only store non-negative numbers -- again, the U is for *unsigned*. – Caleb Apr 23 '13 at 05:21
9

Try this

- (IBAction)btnDoSomething:(id)sender {
        int foo = -1;
        NSArray *bar = [[NSArray alloc] initWithObjects:@"1",@"2",@"3", nil];
        if ( foo > (signed)[bar count] ) {
            NSLog(@"Wow, that's messed up.");
        } else {
            NSLog(@"Rock on!");
        }
    }

Working

If you are comparing two different type of variables then it will implicitly convert data type of both variables to higher type of them.

In this example,
variable foo is of type signed int and array count is of unsigned int,
So it will convert data type of foo to unsigned int
then value of foo will become large number, which is smaller than array count 3.

So in this example you need to down cast array count to signed int.


Issues

When your array count exceeds max limit of signed int then after casting it will rounded back like [ -(negative) max limit -> 0 -> + max limit ] which is unexpected result.

Solution

  • Avoid type casting if you are not sure about maximum array length.
  • Do casting if you are sure for limit(maximum array length will not exceeds signed int max limit).

For more details check this
http://visualcplus.blogspot.in/2006/02/lesson-4-casting-data-types.html

Navnath Godse
  • 2,233
  • 2
  • 23
  • 32