54

使用 64 位版本的 iOS,我们不能再使用%dand%u来格式化NSIntegerNSUInteger. 因为对于 64 位,这些是 typedef 到longandunsigned long而不是intand unsigned int

因此,如果您尝试使用 %d 格式化 NSInteger,Xcode 会抛出警告。Xcode 对我们很好,它提供了这两种情况的替代品,它包括一个以 l 为前缀的格式说明符和一个类型转换为 long。那么我们的代码基本上是这样的:

NSLog(@"%ld", (long)i);
NSLog(@"%lu", (unsigned long)u);

如果你问我,那是眼睛疼痛。

几天前,Twitter 上有人提到了格式化有符号%zd变量的格式说明符,以及%tu在 32 位和 64 位平台上格式化无符号变量的格式说明符。

NSLog(@"%zd", i);
NSLog(@"%tu", u);

这似乎有效。我喜欢的不仅仅是类型转换。

但老实说,我不知道为什么这些工作。现在两者对我来说基本上都是神奇的价值。

我做了一些研究,发现z前缀意味着以下格式说明符的大小与size_t. 但我完全不知道前缀是什么t意思。所以我有两个问题:

究竟是什么%zd意思%tu

使用它是否安全,%zd%tu不是苹果建议将类型转换为长?


我知道类似的问题和 Apples 64-Bit Transition guides,它们都推荐这种%lu (unsigned long)方法。我要求替代类型转换。

4

3 回答 3

52

来自http://pubs.opengroup.org/onlinepubs/009695399/functions/printf.html

  • z
    指定以下 [...] 转换说明符应用于一个size_t或相应的有符号整数类型参数;
  • t
    指定以下 [...] 转换说明符应用于一个ptrdiff_t或相应的无符号类型参数;

并来自http://en.wikipedia.org/wiki/Size_t#Size_and_pointer_difference_types

  • size_t用于表示特定实现中任何对象(包括数组)的大小。它用作sizeof运算符的返回类型。
  • ptrdiff_t用于表示指针之间的差异。

在当前的 OS X 和 iOS 平台上,我们有

typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;

where__SIZE_TYPE____PTRDIFF_TYPE__由编译器预定义。对于 32 位,编译器定义

#define __SIZE_TYPE__ long unsigned int
#define __PTRDIFF_TYPE__ int

对于 64 位,编译器定义

#define __SIZE_TYPE__ long unsigned int
#define __PTRDIFF_TYPE__ long int

(这可能在 Xcode 版本之间发生了变化。受@user102008 评论的启发,我已经用 Xcode 6.2 进行了检查并更新了答案。)

所以ptrdiff_tandNSInteger都被定义为相同的类型int在 32 位和long64 位上。所以

NSLog(@"%td", i);
NSLog(@"%tu", u);

在所有当前的 iOS 和 OS X 平台上正常工作并编译而不会出现警告。

size_t并且NSUInteger在所有平台上都具有相同的大小,但它们的类型不同,所以

NSLog(@"%zu", u);

实际上在编译 32 位时会发出警告。

但是这种关系在任何标准中都不是固定的(据我所知),因此我 不会认为它是安全的(与假设long具有相同大小的指针不被认为是安全的含义相同)。将来可能会破裂。

我所知道的类型转换的唯一替代方法是使用预处理器宏对“为 arm64 和 32 位架构进行编译时的基础类型”的答案:

// In your prefix header or something
#if __LP64__
#define NSI "ld"
#define NSU "lu"
#else
#define NSI "d"
#define NSU "u"
#endif

NSLog(@"i=%"NSI, i);
NSLog(@"u=%"NSU, u);
于 2013-09-19T12:36:34.933 回答
11

我更喜欢只使用一个NSNumber

NSInteger myInteger = 3;
NSLog(@"%@", @(myInteger));

这并非在所有情况下都有效,但我已将大部分 NS(U)Integer 格式替换为上述格式。

于 2014-02-25T17:43:01.943 回答
2

根据Building 32-bit Like 64-bit,另一种解决方案是定义NS_BUILD_32_LIKE_64宏,然后您可以简单地使用%ldand%lu说明符,NSInteger有无NSUInteger强制转换和无警告。

于 2015-04-02T22:37:06.323 回答