我最近浪费了大约半个小时来追踪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)”。
显然,使用long long
打印%d
是一个错误,但有人可以解释为什么会导致text
打印为空吗?
答案 0 :(得分:9)
你刚刚搞砸了堆栈上的内存对齐。我假设你使用最新的Apple产品和x86处理器。考虑到这些假设,您的堆栈在两种情况下都是如此:
| stack | first | second | +---------------------+-------+--------+ | 123 | | %d | +---------------------+ %lld +--------+ | 0 | | %@ | +---------------------+-------+--------+ | pointer to text | %@ |ignored | +---------------------+-------+--------+
在第一种情况下,你把堆栈8个字节然后4个字节。并且指示NSLog从堆栈中取回12个字节(%lld
为8个字节,%@
为4个字节)。
在第二种情况下,您指示NSLog首先占用4个字节(%d
)。由于您的变量长度为8个字节并且保持非常小的数字,因此上面的4个字节将为0.然后当NSLog尝试打印文本时,它将从堆栈中获取nil
。
由于向nil
发送消息在Obj-C NSLog中有效,因此只需向description:
发送nil
即可获取任何内容,然后打印(空)。
最后,由于Objective-C只是带有附加功能的C,调用者可以清理整个这个混乱。
答案 1 :(得分:1)
varargs的实现方式与系统有关。但可能发生的是,参数被连续存储在缓冲区中,即使参数可能是不同的大小。因此,参数的前8个字节(假设是long long int
的大小)是long long int
,接下来的4个字节(假设系统中指针的大小)是{{ 1}}指针。
然后当你告诉函数它需要一个NSString
然后一个指针时,它期望前4个字节是int
(假设它是int
的大小)并且接下来的4个字节是指针。由于系统上特定的字节顺序和参数排列,int
的前4个字节恰好是数字中最不重要的字节,所以它打印123.然后对于对象指针,它会读取下一个字节4个字节,在这种情况下是数字的最高有效字节,全为0,因此被解释为long long int
指针。实际指针永远不会被读取。