4

我有一个针对 iOS SDK 6.1 的通用 iOS 应用程序,编译器设置为Apple LLVM compiler 4.2。当我在我的代码中放置一个断点并运行以下命令时,我得到了奇怪的结果sin(int)

作为参考,sin(70)= 0.7739(70 以弧度为单位)。

(lldb) p (double)sin(70)
(double) $0 = -0.912706376367676 // initial value
(lldb) p (double)sin(1.0)
(double) $1 = 0.841470984807897 // reset the value sin(int) will return
(lldb) p (double)sin(70)
(double) $2 = 0.841470984807905 // returned same as sin(1.0)
(lldb) p (double)sin(70.0)
(double) $3 = 0.773890681557889 // reset the value sin(int) will return
(lldb) p (double)sin(70)
(double) $4 = 0.773890681558519
(lldb) p (double)sin((float)60)
(double) $5 = -0.304810621102217 // casting works the same as appending a ".0"
(lldb) p (double)sin(70)
(double) $6 = -0.30481062110269
(lldb) p (double)sin(1) 
(double) $7 = -0.304810621102223 // every sin(int) behaves the same way

观察:

  • 调试会话中的第一个值sin(int)始终是-0.912706376367676
  • sin(int)将始终返回与上次执行时返回的值相同的值sin(float)
  • 如果我ppo, 或expr(例如 expr (double)sin(70)) 替换,我会得到完全相同的结果。

为什么调试器会这样?

这是否意味着每次调用函数时都应该对每个参数进行类型转换?

NSLog 的一些更有趣的行为:

(lldb) expr (void)NSLog(@"%f", (float)sin(70))
0.000000 // new initial value
(lldb) expr (void)NSLog(@"%f", (float)sin(70.0))
0.773891
(lldb) expr (void)NSLog(@"%f", (float)sin(70))
0.000000 // does not return the previous sin(float) value
(lldb) p (double)sin(70)
(double) $0 = 1.48539705402154e-312 // sin(int) affected by sin(float) differently
(lldb) p (double)sin(70.0)
(double) $1 = 0.773890681557889
(lldb) expr (void)NSLog(@"%f", (float)sin(70))
0.000000 // not affected by sin(float)
4

1 回答 1

4

您正在走进 C 语言默认参数提升的美妙世界。记住,lldb 不知道参数类型或返回类型sin()是什么。正确的原型是double sin (double). 当你写

(lldb) p (float) sin(70)

这有两个问题。首先,您提供一个整数参数,C 默认提升规则将把它作为int一个 4 字节值传递给相关架构。 double,除了是 8 字节之外,是一种完全不同的编码。sin垃圾输入也是如此。其次,在这些架构上sin()返回一个double或 8 字节的值,但您告诉 lldb 获取其中的 4 个字节并做一些有意义的事情。如果你调用p (float)sin((double)70)了(所以只有返回类型不正确)lldb 会打印一个无意义的值,比如 9.40965e+21 而不是 0.773891。

当你写

(lldb) p (double) sin(70.0)

你修正了这些错误。浮点类型的默认 C 提升是将其作为double. 如果您正在调用sinf(),您会遇到问题,因为该函数只需要一个float.

如果您想为 lldb 提供适当的原型sin()而不担心这些问题,这很容易。将此添加到您的~/.lldbinit文件中,

settings set target.expr-prefix ~/lldb/prefix.h

(我有一个~/lldb目录,用于存储有用的 python 文件和类似的东西)~/lldb/prefix.h并将读取

extern "C" {
int strcmp (const char *, const char *);
void printf (const char *, ...);
double sin(double);
}

(你可以看到我的前缀文件中也有原型,strcmp()所以printf()我不需要强制转换这些。)你不想在这里放太多东西——这个文件被添加到你在 lldb 中评估的每个表达式之前如果你把所有的原型都/usr/include放在那里,它会减慢你的表达式评估。

将该原型添加到我的target.expr-prefix设置中:

(lldb) p sin(70)
(double) $0 = 0.773890681557889
于 2013-04-04T19:53:17.713 回答