在gdb中打印C++ std::set字符串的第一个元素?

4
Element at index in a std::set?所述,std::set没有直接的随机访问索引方式。因此,在这里我尝试使用它的.begin()方法返回一个迭代器...下面是一个简单的最小示例:
// g++ --std=c++11 -g test.cpp -o test.exe

#include <iostream>
#include <set>

int main()
{
  std::set<std::string> my_set;
  my_set.insert("AA");
  my_set.insert("BB");
  std::cout << "Hello World!" << std::endl;
  return 0;
}

我最终想做的是在我的实际问题中使用 dprintf 类型的断点-我不想更改代码,因此不添加额外的迭代器变量。由于使用格式说明符,其中%s用于C样式字符串,因此我最终不仅需要引用的第一个元素,而且还需要在其上调用.c_str()-并且所有这些都需要在一行中完成(在我的实际问题中,我有一个自定义String类的,它具有用于获取C样式字符串的自定义方法)。
问题是,我找不到访问第一个元素的正确语法:
$ gdb --args ./test.exe
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Reading symbols from ./test.exe...done.
(gdb) b test.cpp:11
Breakpoint 1 at 0x8048bf8: file test.cpp, line 11.
(gdb) r
Starting program: /tmp/test.exe 

Breakpoint 1, main () at test.cpp:11
11    std::cout << "Hello World!" << std::endl;
(gdb) p my_set
$1 = std::set with 2 elements = {[0] = "AA", [1] = "BB"}
(gdb) p my_set.begin()
Cannot evaluate function -- may be inlined
(gdb) p my_set->begin()
Cannot resolve method std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::begin to any overloaded instance
(gdb) printf "%s", my_set.begin()
Cannot evaluate function -- may be inlined

在类似这样的上下文中,使用GDB的printf,是否可以打印出std :: set的第一个值,而无需更改代码(例如添加迭代器变量)?

1
看起来你可以适应脚本来转储STL容器内容,https://gist.github.com/skyscribe/3978082 - Andrey Starodubtsev
非常感谢您的回复,@AndreyStarodubtsev - 但是,我仍然无法访问第一个元素,“.begin()”仍然显示“无法评估函数--可能已内联”,“my_set[0]”仍然显示“当前上下文中没有符号“operator[]”。”我看到有一个“pset”命令,但也无法使其工作。有什么建议吗? - sdaau
1
我可能错得很惨,但我猜编译器需要看到函数的使用才能将其放入可执行文件中。尝试在代码中的任何位置调用my_set.begin(),看看它是否有效。 - Amadeus
谢谢,@Amadeus - 你说得对:我添加了一些用法,如 std::set<std::string>::iterator my_iter; my_iter = my_set.begin(); cout << &my_iter; ,这确实使 my_set.begin()gdb 中打印出来了!但这似乎需要代码更改,我想避免... - sdaau
1个回答

0

好的,多亏了@AndreyStarodubtsev和@Amadeus的评论,我有所收获。

首先,正如@Amadeus所指出的那样 - 事实证明,确实必须在程序中引用某种类型的引用,否则它可能会被优化掉。因此,当调试时,OP示例将会给出以下结果:

(gdb) p ((std::string*)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()
A syntax error in expression, near `)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()'.
(gdb) p std::string
No symbol "string" in namespace "std".

...然而,只需要一个变量引用就足以解决这个问题:

// g++ --std=c++11 -g test.cpp -o test.exe

#include <iostream>
#include <set>

std::string aa; // just to have reference to std::string

int main()
{
  std::set<std::string> my_set;
  my_set.insert("AA");
  my_set.insert("BB");
  std::cout << "Hello World!" << std::endl;
  return 0;
}

...虽然这是代码更改,但我不必添加引用迭代器的新实际代码行。然后,gdb 会话如下所示:

$ gdb --args ./test.exe
...
Reading symbols from ./test.exe...done.
(gdb) b 13
Breakpoint 1 at 0x8048c38: file test.cpp, line 13.
(gdb) r
Starting program: /tmp/test.exe 

Breakpoint 1, main () at test.cpp:13
13    std::cout << "Hello World!" << std::endl;
(gdb) p ((std::string*)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()
$1 = 0x804d014 "AA"
(gdb) printf "'%s'\n", ((std::string*)(my_set._M_t._M_impl._M_header._M_left+1))->c_str()
'AA'

要访问my_set的第一个节点(显然是一个"红黑树"的节点),我参考了@AndreyStarodubtsev提到的https://gist.github.com/skyscribe/3978082 gdb脚本,特别是pset函数(由于对std::string的优化引用,可能会导致语法错误)。


1
你可以尝试在一个新文件中放置 std::string aa;,编译它,并链接两个文件,即这个新文件和 test.cpp。这样,你就不需要改变 test.cpp。 - Amadeus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接