ChatGPT解决这个技术问题 Extra ChatGPT

NSLog/printf specifier for NSInteger?

A NSInteger is 32 bits on 32-bit platforms, and 64 bits on 64-bit platforms. Is there a NSLog specifier that always matches the size of NSInteger?

Setup

Xcode 3.2.5

llvm 1.6 compiler (this is important; gcc doesn't do this)

GCC_WARN_TYPECHECK_CALLS_TO_PRINTF turned on

That's causing me some grief here:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    @autoreleasepool {
        NSInteger i = 0;
        NSLog(@"%d", i);
    }
    return 0;
}

For 32 bit code, I need the %d specifier. But if I use the %d specifier, I get a warning when compiling for 64 bit suggesting I use %ld instead.

If I use %ld to match the 64 bit size, when compiling for 32 bit code I get a warning suggesting I use %d instead.

How do I fix both warnings at once? Is there a specifier I can use that works on either?

This also impacts [NSString stringWithFormat:] and [[NSString alloc] initWithFormat:].


L
Lily Ballard

Updated answer:

You can make use of the z and t modifiers to handle NSInteger and NSUInteger without warnings, on all architectures.

You want to use %zd for signed, %tu for unsigned, and %tx for hex.

This information comes courtesy of Greg Parker.

Original answer:

The official recommended approach is to use %ld as your specifier, and to cast the actual argument to a long.


This is definitely the way to go, but I think I might use static inline NSIntToLong(NSInteger i) {return (long)i;}. This avoids disabling type checking completely (i.e. if the type of i changes).
Good thinking by @steven-fisher. Avoid warning with: static inline long NSIntToLong(NSInteger i) {return (long)i;}
You can also create an NSNumber and log that. NSLog(@"%@",@(mynsint)); stackoverflow.com/questions/20355439/…
@KevinBallard This should not be a serious performance issue. You should not use lots of NSLog in production code anyway. If you have to log lots of stuff for some reason, do it on a separate thread.
As of Xcode 9.3 there is a warning when using NSInteger as a format argument with %zd: Values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead
g
gnasher729

The accepted answer is absolutely reasonable, it is standard conforming, and correct. The only problem is that it doesn't work anymore, which is completely Apple's fault.

The format %zd is the C/C++ standard format for size_t and ssize_t. Like NSInteger and NSUInteger, size_t and ssize_t are 32 bit on a 32 bit system, and 64 bit on a 64 bit system. And that's why printing NSInteger and NSUInteger using %zd worked.

However, NSInteger and NSUInteger are defined as "long" on a 64 bit system, and as "int" on a 32 bit system (which is 64 vs 32 bit). Today, size_t is defined on "long" on all systems, which is the same size as NSInteger (either 64 or 32 bit), but a different type. Either Apple's warnings have changed (so it doesn't allow passing the wrong type to printf, even though it has the right number of bits), or the underlying types for size_t and ssize_t have changed. I don't know which one, but %zd stopped working some time ago. There is no format today that will print NSInteger without warning on both 32 and 64 bit systems.

So the only thing you can do unfortunately: Use %ld, and cast your values from NSInteger to long, or from NSUInteger to unsigned long.

Once you don't build for 32 bit anymore, you can just use %ld, without any cast.


c
cat

The formatters come from the standard UNIX/POSIX printf function. Use %lu for unsigned long, %ld for long, %lld for long long, and %llu for unsigned long long. Try man printf on the console, but on Mac it is incomplete. The linux manpages are more explicit http://www.manpages.info/linux/sprintf.3.html

Both warnings can only be fixed by NSLog(@"%lu", (unsigned long)arg); combined with a cast as the code will be compiled in 32 AND 64 bit for iOS. Otherwise each compilation creates a separate warning.