20

For some reason, I get a compilation error when I try to do the following:

NSLog(@"row: %lu", indexPath.row);

where row is of type NSUInteger. The error I get is

Conversion specifies type 'unsigned long' but the argument has type 'NSUInteger' (aka 'unsigned int')

I can do the following with no compilation errors:

NSString * string = [NSString stringWithFormat:@"row: %lu", indexPath.row];

I'm using the exact same format string and substitution argument in both cases, but why does NSLog freak out while -stringWithFormat: seems to be perfectly content? My compiler is LLVM 1.6.

CIFilter
  • 8,647
  • 4
  • 46
  • 66
  • Maybe the difference is that `NSLog()` is a straight C-style function call, while `-stringWithFormat:` is an Objective-C method call; so the compiler might need different mechanisms for warning about mismatches between format string and actual arg types. – David Gelhar Feb 15 '11 at 03:43
  • The Cocoa APIs must be doing some sort of automatic type conversion, which is why I've gotten away with this for so long I imagine! – CIFilter Feb 15 '11 at 03:54

3 Answers3

35

All devices that iOS currently runs on are 32-bit. If you want to silence the warning:

NSLog(@"row: %lu", (unsigned long)indexPath.row);

[Edit: As of the iPhone 5s, it is no longer true that iOS is always 32-bit.]

Wevah
  • 28,182
  • 7
  • 83
  • 72
  • ? Really? I must be crazy. I could have sworn that iOS was 64-bit... I'm extremely embarrassed now. I guess I should just hang up the towel if I don't even know the architecture of the devices I'm developing on... Nevertheless, that still doesn't answer my question about `-stringWithFormat:`. – CIFilter Feb 15 '11 at 00:15
  • I admit that I don't know why it doesn't throw the warning there. It could just be a Clang 1.6 issue, but I'm not sure. – Wevah Feb 15 '11 at 00:19
  • The easiest way to tell would be to print `sizeof(int *)`. If it says 4, it's 32-bit. If it says 8, it's 64-bit. AFAIK, all iOS hardware is 32-bit; you might be thinking of running the Simulator in 64-bit x86, or maybe the fact that the iOS APIs that also exist on Mac OS X are a subset of those that exist in 64-bit on Mac OS X. – Peter Hosey Feb 15 '11 at 01:09
  • @Peter Hosey: I think my confusion came exactly from the fact that the common APIs exist in 64-bit form in Mac OS X. Anyway, stupidity aside, I still am very curious why only `NSLog` seems to complain about this issue. – CIFilter Feb 15 '11 at 03:02
  • 3
    @LucasTizma: It's because you use -Wformat (as you should). With that, Clang will warn when you pass an argument whose type doesn't match the format specifier that consumes it. This is a feature, particularly in variadic arguments, where there's no type safety. In assignments and non-variadic arguments, implicit casts usually do the right thing with no need for a warning. – Peter Hosey Feb 15 '11 at 05:59
  • @PeterHosey: I can't manage to get `+[NSString stringWithFormat:]` to give any warnings; see [LLVM bug 10275](http://llvm.org/bugs/show_bug.cgi?id=10275). According to 10274, it's a surprise that format checks work for NSLog. – tc. Apr 04 '12 at 17:46
  • 1
    @tc.: With Xcode 4.3.1's Clang, it does indeed work for `NSLog` but not `stringWithFormat:`, even though both are declared with the relevant attribute. Weird. – Peter Hosey Apr 04 '12 at 17:54
  • 1
    I added an answer with the alternative of using `NS_BUILD_32_LIKE_64`, which has the advantage of not requiring any code changes. – Quinn Taylor Aug 14 '12 at 20:30
  • I just do `%ju` and cast to `uintmax_t`. When `NSUInteger` goes 128-bit in a century, I'm ready for it. – Jeremy W. Sherman Sep 20 '12 at 23:46
  • 1
    iOS 7 and the iPhone 5S (and presumably the next iPad) are 64-bit, so the time to think about this has definitely arrived. – Jacob Pritchett Sep 16 '13 at 23:42
7

I've run into this same issue, and although @Wevah is correct and his answer works just fine, there's another option that doesn't require any code changes. See the following Apple documentation for details:

String Programming Guide | Platform Dependencies

64-Bit Transition Guide for Cocoa | Building 32-Bit Like 64-Bit

The NS_BUILD_32_LIKE_64 preprocessor macro is quite helpful. You can set it in your Xcode project settings (under GCC_PREPROCESSOR_DEFINITIONS) or just put #define NS_BUILD_32_LIKE_64 1 in your precompiled header (.pch) file. In my app, this eliminated 11 warnings without any code changes.

This works because unsigned int and unsigned long are the same size (4 bytes) on iOS, so changing the typedef of NSUInteger makes the compiler (and the developer) happy, but the hardware doesn't care, since it's just doing integer math in both cases. :-)

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
0

Apple documentation recommends casting the 64 bit value to a 32 bit value using %lu and %ld. This poses a problem if you actually use the extra 32 bits. Format strings %qu and %qd specify a 64 bit value (unsigned and signed respectively). If you want code that will compile in either mode, then values declared as NSUInteger or NSInteger will have to be cast to a UInt64 or SInt64 in the parameter list to avoid the warning.

carmin
  • 421
  • 3
  • 5