我使用的是 Mac OS Sonoma 14.5,通过 x 代码工具安装了 LLDB 1500.0.404.7。我注意到我的 LLDB 的行为方式相当令人困惑。它能够完整地打印出一些 std:container,但无法打印出这些相同容器的子组件。特别是我能够执行以下
p primary_map
命令
(lldb) p primary_map
(std::unordered_map<int, std::unordered_set<int> >) size=2 {
[0] = {
first = 1
second = size=2 {
[0] = 5
[1] = 4
}
}
[1] = {
first = 0
second = size=2 {
[0] = 2
[1] = 1
}
}
}
显然 LLDB 可以向我展示整个地图。但出于某种原因,如果我执行
p primary_map[0]
(lldb) p primary_map[0]
error: Couldn't lookup symbols:
__ZNSt3__113unordered_mapIiNS_13unordered_setIiNS_4hashIiEENS_8equal_toIiEENS_9allocatorIiEEEES3_S5_NS6_INS_4pairIKiS8_EEEEEixEOi
LLDB 无法呈现数据结构的子组件。
我想知道,为什么会发生这种情况,将来如何预防它,如果无法预防,什么是最好的解决方法。
我有一段示例代码。它基本上通过读取文件的内容来创建整数到整数集的映射。特别是文件中的行数和每行的整数数在编译时是未知的,只能在运行时通过读取文件本身来发现(不知怎的,我认为这很重要)。
工作驱动程序.cpp
#include <fstream>
#include <string>
#include <unordered_set>
#include <unordered_map>
using namespace std;
unordered_map<int, unordered_set<int> > primary_map;
int main() {
ifstream input("sample_data_working.txt");
int row_count; int col_count; int holder;
input >> row_count; input >> col_count;
for (int i =0; i < row_count; i++) {
primary_map[i] = unordered_set<int>{};
for (int j = 0; j < col_count; j++) {
input >> holder;
primary_map[i].insert(holder);
}
}
return 0;
}
该文件如下,其标题是一对数字(行数,列数),然后是包含数字的行。
样本数据工作.txt
2 2
1 2
4 5
我使用以下命令编译我的代码:
g++ -std=c++14 -g -O0 -fstandalone-debug working_driver.cpp -o working_driver
,它实际上调用了 clang(因为 Mac OS),版本为:Apple clang version 15.0.0 (clang-1500.3.9.4)。
然后执行
lldb working_driver
来启动 LLDB 实例。从这里,我通过 b 23
在 return 语句上设置一个断点,然后执行 r
命令,该命令继续前进并让我到达断点。在终端中,这应该类似于:
➜ differentiation git:(main) ✗ lldb working_driver
(lldb) target create "working_driver"
Current executable set to '/Users/sidharthghoshal/cp_training/USACO/python3/chapter4/2/stall4/differentiation/working_driver' (x86_64).
(lldb) b 23
Breakpoint 1: where = working_driver`main + 465 at working_driver.cpp:23:5, address = 0x0000000100000b91
(lldb) r
Process 3140 launched: '/Users/sidharthghoshal/cp_training/USACO/python3/chapter4/2/stall4/differentiation/working_driver' (x86_64)
Process 3140 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000b91 working_driver`main at working_driver.cpp:23:5
20 primary_map[i].insert(holder);
21 }
22 }
-> 23 return 0;
24 }
Target 0: (working_driver) stopped.
正是在这一点上,混乱随之而来。
p primary_map
完全按照这个问题顶部的预期工作,但 p primary_map[0]
却没有。
我过去见过
p primary_map[0]
命令在相同的结构 unordered_map<int, unordered_set<int> >
上工作,但形式略有不同(即循环中的硬编码上限 row_count
、col_count
)。因此,stackoverflow 上说 primary_map[0]
定义不明确的帖子并不完全令人满意,因为该命令有时确实有效,只是在这种情况下不起作用。不知何故,“运行时发现的循环边界”似乎是一个令人困惑的问题。
通常讨论的修复方法是编写自定义分配器或自定义解析器,我不明白的是为什么我必须这样做?显然,LLDB 为
unordered_map<int, unordered_set<int> >
提供的内置解析器在某些情况下就足够了。只是不是这个例子。我想知道一个为什么。然后我会深深感激如何。
评分最高的答案这里澄清了很多。特别是
frame var primary_map[0]
的行为确实符合预期。似乎 LLDB 足够聪明,知道为我的某些代码打印 frame var
,但其他时候它不够聪明,无法 frame var
,然后只是发出奇怪的符号错误。我不清楚运行时定义的循环边界与从 frame var
切换到 expr
有什么关系,但现在联系变得更加清晰了。我认为原因是,当您在控制台中输入
primary_map[0]
时,您正在编写一些调用
library代码的内容。我的意思是,您使用的
operator[]
不是您在 C 样式数组上使用的内置 operator[]
。您需要 compile
primary_map[0]
调试器才能运行它。另一方面,如果您查看 primary_map
类的实现,您可能会了解如何深入数据结构以提取您想要的内容,但我不确定它总是可行的,并且这肯定不容易。
这是一个更简单的例子。如果您使用调试符号编译此代码,#include <vector>
int main() {
std::vector<int> v{17,2,3};
return 0;
}
然后在
return
上放置一个断点,您会发现在控制台中键入
v[0]
会给出完全相同的错误。但是,如果您研究 std::vector
的实现,您会发现实际值存储在位于
v._M_impl._M_start
的 C 数组中,因此如果您输入 v._M_impl._M_start[0]
/v._M_impl._M_start[1]
/ v._M_impl._M_start[2]
在控制台中,确实会按预期打印 17
/2
/3
。我在哪里看到v
的值存储在
v._M_impl._M_start
中?好吧,最简单的方法是在 v
上放置一个观察器,调试器将显示如下:Expression: v
*- Result: size=3
*- [0]: 1
*- [1]: 2
*- [2]: 3
*+ [raw]: std::vector<int, std::allocator<int> >
raw
就是您想要研究的内容,所以让我们扩展它:
Expression: v
*- Result: size=3
*- [0]: 1
*- [1]: 2
*- [2]: 3
*- [raw]: std::vector<int, std::allocator<int> >
- std::_Vector_base<int, std::allocator<int> >: {...}
- _M_impl: {...}
- std::_Vector_base<int, std::allocator<int> >::_Vector_impl_data: {_M_start:0x000055555556d2b0, _M_finish:0x000055555556d2bc, ...}
- _M_start: 0x000055555556d2b0
*- *_M_start: 17
+ _M_finish: 0x000055555556d2bc
+ _M_end_of_storage: 0x000055555556d2bc
在这里,搜索
17
,您会发现它等于
*_M_start
,因此 _M_start
是包含数据的 C 数组。那是在 _M_impl
下,在 v
下。