Why does an NSInteger variable have to be cast to long when used as a format argument?

NSInteger myInt = 1804809223;
NSLog(@"%i", myInt); <====

The code above produces an error:

Values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead

The corrected NSLog message is actually NSLog(@"%lg", (long) myInt);. Why do I have to convert the integer value of myInt to long if I want the value to display?

46456 次浏览

如果在 OS X (64位)上编译,就会得到这个警告,因为在该平台上,NSInteger被定义为 long,是一个64位整数。另一方面,%i格式适用于32位的 int。所以格式和实际参数的大小不匹配。

由于 NSInteger是32位或64位的,具体取决于平台,因此编译器建议使用 一般来说,为 long增加一个演员阵容。

更新: 由于 iOS7现在也支持64位,所以在编译时也会收到同样的警告 在 iOS 上。

如果格式说明符与数据类型匹配,则不必强制转换为任何内容。有关如何根据本机类型定义 NSInteger的详细信息,请参阅 Martin R 的答案。

因此,对于打算为64位环境构建的代码,您可以像下面这样编写日志语句:

NSLog(@"%ld",  myInt);

而对于32位环境,您可以编写:

NSLog(@"%d",  myInt);

没有石膏也行。

无论如何,使用强制转换的一个原因是,好的代码倾向于跨平台移植,如果显式强制转换变量,它将在32位和64位上干净地编译:

NSLog(@"%ld",  (long)myInt);

注意,这不仅适用于 NSLog 语句(毕竟它只是调试辅助工具) ,也适用于 [NSString stringWithFormat:]和各种派生消息(它们是生产代码的合法元素)。

不要将 NSInteger 传递给 NSLog,只要传递一个 NSNumber 即可。这将绕过所有的强制转换并选择正确的字符串格式说明符。

NSNumber foo = @9000;
NSLog(@"foo: %@", foo);
NSInteger bar = 9001;
NSLog(@"bar: %@", @(bar));

它也为 NSUIntegers 工作,而不必担心这一点。 请参阅在64位/32位混合环境中的 NSInteger 和 NSUInteger 的答案

OS X 使用几种数据类型(NSInteger、 NSUInteger、 CGFloat 和 CFIndex)提供一致的方法来表示32位和64位环境中的值。在32位环境中,NSInteger 和 NSUInteger 分别定义为 int 和 unsignedint。在64位环境中,NSInteger 和 NSUInteger 分别被定义为 long 和 unsignedlong。为了避免根据平台使用不同的 printf 样式的类型说明符,可以对32位和64位环境使用 这个链接中显示的说明符。

它在使用 NSLog(@"%ld", (long)myInt);时保持警告,但是在 iOS10中更改为 long myInt = 1804809223;后停止警告。