2009-08-04 83 views
2

最近我浪费了大约半小时追踪到的NSLog这种奇怪的行为(...):NSLog(...)不正确的格式说明符影响其他变量?

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

线(A)打印预计 “数= 123,文本= ABC”,但线(B )打印“num = 123,text = (null)”。

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

+0

如果使用-Wall选项进行编译,编译器会警告您这类问题;我也强烈建议 - 错误,所以警告总是打破构建。 – 2009-08-04 19:56:16

回答

9

你刚刚搞乱了堆栈上的内存对齐。我认为比你使用x86处理器的最新Apple产品。考虑到这些假设,考虑你的筹码看起来,在这两种情况下:

 
    |  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

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

然后当你告诉它预期的int,然后指针的函数,它预计前4个字节是int(假设这是一个int的大小),并在接下来的4个字节的指针。由于系统中参数的特定排序和排列,因此long long int的前4个字节恰好是数字的最低有效字节,因此它会打印123.然后,对于对象指针,它会读取下一个4个字节,其中在这种情况下,您的数字的最高有效字节全部为0,因此会被解释为nil指针。实际的指针永远不会被读取。