2

我最近浪费了大约半个小时来追踪 NSLog(...) 中的这种奇怪行为:

NSString *text = @"abc";
long long num = 123;
NSLog(@"num=%lld, text=%@",num,text); //(A)
NSLog(@"num=%d, text=%@",num,text); //(B)

行 (A) 打印预期的“num=123, text=abc”,但行 (B) 打印“num=123, text= (null) ”。

显然,打印 a long longwith%d是一个错误,但有人可以解释为什么它会导致text打印为 null 吗?

4

2 回答 2

9

您只是弄乱了堆栈上的内存对齐方式。我假设您使用最新的 Apple 产品和 x86 处理器。考虑到这些假设,您的堆栈在两种情况下都是这样的:

   | 堆栈 | 第一 | 第二 |
   +---------+--------+--------+
   | 123 | | %d |
   +----------+ %lld +--------+
   | 0 | | %@ |
   +---------+--------+--------+
   | 指向文本的指针 | %@ |忽略 |
   +---------+--------+--------+  

在第一种情况下,您将 8 个字节放入堆栈,然后放入 4 个字节。然后 NSLog 被指示从堆栈中取回 12 个字节(8 个字节%lld和 4 个字节%@)。

在第二种情况下,您指示 NSLog 首先占用 4 个字节 ( %d)。由于您的变量是 8 个字节长并且包含非常小的数字,它的高 4 个字节将为 0。然后当 NSLog 将尝试打印文本时,它将nil从堆栈中获取。

由于nil在 Obj-C NSLog 中发送消息是有效的,因此description:发送消息nil可能一无所获,然后打印(空)。

最后,由于 Objective-C 只是带有添加的 C,调用者清理了整个混乱。

于 2009-08-04T18:50:56.760 回答
1

可变参数的实现方式取决于系统。但是可能发生的情况是参数连续存储在缓冲区中,即使参数可能大小不同。因此,参数的前 8 个字节(假设是 a 的大小long long int)是long long int,接下来的 4 个字节(假设是系统上指针的大小)是NSString指针。

然后,当您告诉函数它需要一个int然后是一个指针时,它希望前 4 个字节是int(假设是 a 的大小int),接下来的 4 个字节是指针。由于系统上的特殊字节顺序和参数排列,前 4 个字节long long int恰好是数字的最低有效字节,因此它打印 123。然后对于对象指针,它读取接下来的 4 个字节,其中这种情况是数字的最重要字节,全为 0,因此被解释为nil指针。实际的指针永远不会被读取。

于 2009-08-04T18:57:22.393 回答