我有一个针对iOS SDK 6.1的通用iOS应用,并且编译器设置为 Apple LLVM编译器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)
返回的相同值。p
替换为po
或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)
答案 0 :(得分:4)
你正在进入C中默认参数促销的精彩世界。请记住,lldb不知道sin()
的参数类型或返回类型是什么。正确的原型是double sin (double)
。当你写
(lldb) p (float) sin(70)
这有两个问题。首先,您提供了一个整数参数,C默认促销规则将把它作为int
传递给所讨论的体系结构上的4字节值。除了8字节之外,double
是完全不同的编码。所以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