NSInteger的NSLog/printf格式说明符是什么?

140

NSInteger 在 32 位平台上是32位,在 64 位平台上是64位。是否有一个NSLog的说明符总是与 NSInteger 的大小匹配?

设置

  • Xcode 3.2.5
  • llvm 1.6 编译器 (这很重要;gcc 不支持此功能)
  • GCC_WARN_TYPECHECK_CALLS_TO_PRINTF 打开

这让我感到有些苦恼:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    @autoreleasepool {
        NSInteger i = 0;
        NSLog(@"%d", i);
    }
    return 0;
}
对于32位代码,我需要使用%d说明符。但如果我使用%d,当编译64位时会收到警告建议改用%ld
如果我使用%ld以匹配64位大小,则在编译32位代码时会收到警告建议改用%d
如何同时修复两个警告?是否有一个说明符可以在任何情况下都适用?
这也影响[NSString stringWithFormat:][[NSString alloc] initWithFormat:]
3个回答

308

更新的回答:

您可以使用zt修饰符,在所有架构上处理NSIntegerNSUInteger,避免出现警告。

您应该使用%zd表示有符号整数,%tu表示无符号整数,%tx表示十六进制。

这些信息来自Greg Parker


原始回答:

官方推荐的方法是使用%ld作为您的说明符,并将实际参数强制转换为long


6
这绝对是正确的方法,但我认为我可能会使用 static inline NSIntToLong(NSInteger i) {return (long)i;}。这样可以避免完全禁用类型检查(即如果 i 的类型发生更改)。 - Steven Fisher
3
@steven-fisher 的想法很好。避免警告的方法是:static inline long NSIntToLong(NSInteger i) {return (long)i;} - Erik
3
你也可以创建一个NSNumber并进行日志记录。 NSLog(@"%@",@(mynsint)); 详情请见:https://dev59.com/nmIj5IYBdhLWcg3wWT6c - orkoden
2
@KevinBallard 这不应该是一个严重的性能问题。在生产代码中,你本就不应该频繁地使用NSLog。如果你因为某种原因需要记录大量信息,请在一个单独的线程中进行。 - orkoden
5
从Xcode 9.3开始,在使用%zd作为格式参数时,如果使用NSInteger类型,会收到一条警告:Values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead。建议添加一个显式强制类型转换为long - Rob MacEachern
显示剩余17条评论

3
接受的答案是完全合理的,符合标准且正确。唯一的问题是它不再起作用,这完全是苹果的错。
格式%zd是size_t和ssize_t的C / C ++标准格式。与NSInteger和NSUInteger一样,size_t和ssize_t在32位系统上为32位,在64位系统上为64位。这就是为什么使用%zd打印NSInteger和NSUInteger有效的原因。
然而,NSInteger和NSUInteger在64位系统上被定义为“long”,在32位系统上被定义为“int”(即64位与32位)。今天,size_t在所有系统上都被定义为“long”,这与NSInteger相同的大小(64位或32位),但是类型不同。无论是Apple的警告已更改(因此它不允许将错误的类型传递给printf,即使它具有正确的位数),还是size_t和ssize_t的基础类型已更改。我不知道哪一个,但%zd已经停止工作了一段时间。今天没有任何格式可以在32位和64位系统上打印NSInteger而不会出现警告。
所以您唯一能做的事情是:使用%ld,并将您的值从NSInteger转换为long,或从NSUInteger转换为unsigned long。
一旦您不再为32位构建,就可以只使用%ld而无需进行任何转换。

1

格式化程序来自标准的UNIX/POSIX printf函数。使用%lu表示unsigned long,%ld表示long,%lld表示long long,%llu表示unsigned long long。在控制台上尝试man printf,但在Mac上它是不完整的。Linux manpages更加明确 http://www.manpages.info/linux/sprintf.3.html

这两个警告只能通过NSLog(@"%lu", (unsigned long)arg);与强制转换结合使用来修复,因为代码将在iOS的32位和64位中编译。否则每次编译都会创建一个单独的警告。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接