【问题标题】:NSLog(...) improper format specifier affects other variables?NSLog(...) 不正确的格式说明符会影响其他变量吗?
【发布时间】:2009-08-04 18:44:11
【问题描述】:

我最近浪费了大约半个小时来追踪 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)”。

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

【问题讨论】:

  • 如果你用 -Wall 选项编译,编译器会警告你这样的问题;我也强烈推荐使用 -Werror,这样警告总是会破坏构建。
  • @Adam Rosenfield,请注意,对格式检查的支持,ala -Wformat,在 gcc/objc 中一直有点狡猾。这似乎在使用更高版本的编译器时变得更好,但我只是在 Xcode 3.1 下快速检查了一下,并没有发现上述错误。
  • 它不会捕获错误,因为 -Wformat 仅适用于 C 字符串(如在 printf 中)并且完全无法解析 NSString* 对象常量(NSLog 使用)。
  • @johne,Err,不,他们从 leopard 开始就有这个 NSString 说明符,但它不仅仅是靠不住的,它被默默地忽略了。
  • @Jason Coco,对于随 Xcode 发布版本一起提供的预打包编译器,您可能是对的——因为不稳定,他们只是默默地吞下它。但是获取编译器源代码的副本并进行查看。它就在那里,如果你很聪明,你甚至可以让它承认它所做的不仅仅是“忽略”它。我不记得为什么它一开始就不稳定了。不过,总结是这样的:您很可能会在未来看到printf 之类的(CF|NS)String 字符串格式检查,可能是10.6。其中很大一部分已经存在。

标签: objective-c formatting stack printf nslog


【解决方案1】:

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

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

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

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

由于向nil 发送消息在Obj-C 中是有效的,NSLog 只会将description: 发送到nil 可能一无所获然后打印(null)。

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-30
      • 2011-05-14
      • 1970-01-01
      • 2011-05-23
      • 2015-02-19
      相关资源
      最近更新 更多