【问题标题】:Sin(int) is broken in Xcode debugger (lldb)Sin(int) 在 Xcode 调试器 (lldb) 中被破坏
【发布时间】:2013-04-03 16:29:23
【问题描述】:

我有一个面向 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 替换为 poexpr(例如 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)

【问题讨论】:

  • po 命令用于打印对象 - 请尝试使用“p”。此外,sin 接受以弧度为单位的参数,因此 sin(90) 不应为零
  • @Vladimir:p 也会发生这种情况,我更新了代码以反映这一点。我知道 sin 需要弧度,这就是为什么第一个输出很奇怪。
  • 请向 Apple 提交错误!

标签: xcode debugging lldb


【解决方案1】:

您正在走进 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

【讨论】:

  • +1 以获得详尽的解释。尽管我对 C 一无所知,但它是有道理的。
  • 我知道谁会给出答案 :-) - 所以即使我在源文件中包含 math.h,lldb 也不“知道”原型?如果我在我的项目中定义了一个函数double mysin(double x),为什么不会出现这个问题?对不起,如果这些是愚蠢的问题:-)
  • 原型(例如来自<math.h>)由于空间原因不会出现在调试信息中(如果您认为 dSYM 很大今天...) - 但是您自己的函数会生成完整的调试信息,因此 lldb 知道所有参数的类型和返回类型。
猜你喜欢
  • 1970-01-01
  • 2013-03-30
  • 1970-01-01
  • 2020-07-20
  • 2012-07-17
  • 2021-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多