我今天遇到了一个(在我看来)非常奇怪的gdb问题,同时尝试编写更多C ++ - ish版本的我的字符串类的reverse()
方法(当然,所有用于学习) 。尽管有一个正确的默认构造函数将所有成员变量初始化为0,但它们在成员reverse()
- 中根据gdb 开始是乱码(除了调试之外的所有内容实际上都工作正常) - 但不是一个只创建一个空字符串的简单程序。这可能与reverse()
是一个创建自己的类实例的成员函数有关吗?如果没有,为什么不会在一个简单的计划中发生?
g++ -c -o tests.o tests.cpp -Wall -Werror -DDEBUG=0 -O0 -ggdb3
g++ -c -o string.o string.cpp -Wall -Werror -DDEBUG=0 -O0 -ggdb3
g++ -o tests tests.o string.o -Wall -Werror -DDEBUG=0 -O0 -ggdb3
代码:
string::string() : buf(NULL), _length(0), _size(0) {
init();
}
/* This is redundant in this case (right?), but the function is used elsewhere,
and I added the above initialization list while debugging this. */
void string::init() {
this->buf = NULL;
this->_length = 0;
this->_size = 0;
}
string string::reverse(void) const {
string rev;
rev.alloc(this->_length + 1);
for (size_t i=0; i<this->_length; i++) {
...
这是我通过gdb 7.0(在Linux上)运行上面得到的结果:
(对不起横向滚动,但没关系,因为无论如何你都可以看到所有你想要的东西。)
Breakpoint 1, exscape::string::reverse (this=0x7fffffffd580) at string.cpp:368
368 string rev;
(gdb) next
378 rev.alloc(this->_length + 1); <<<< not yet executed when we print below!
(gdb) p rev
$1 = {buf = 0x7fffffffd560 "", _length = 140737488344448, _size = 140737488344128}
(gdb) n
380 for (size_t i=0; i<this->_length; i++) {
(gdb)
381 rev.buf[this->_length-i-1] = this->buf[i];
380 for (size_t i=0; i<this->_length; i++) {
(gdb) p rev
$2 = {buf = 0x7fffffffd560 "P\321`", _length = 140737488344448, _size = 140737488344128}
(gdb) n
381 rev.buf[this->_length-i-1] = this->buf[i];
(gdb)
380 for (size_t i=0; i<this->_length; i++) {
...
384 rev._length = this->_length;
(gdb)
386 }
(gdb) p rev
$3 = {buf = 0x7fffffffd560 "P\321`", _length = 140737488344448, _size = 140737488344128}
(gdb) next
main () at tests.cpp:72
(gdb) p r2 <<<< r2 is the name of the variable returned by reverse() of course
$4 = {buf = 0x60d150 "ABCDEF", _length = 6, _size = 7}
为什么成员变量最终会出现gdb中的乱码值? (顺便说一句,胡言乱语的值总是非常接近this
!在这次运行中,this
== 0x7fffffffd580 == 140737488344448,与_length
相同的值)。该函数完美运行,valgrind从不抱怨,一切都很好......直到我尝试重写方法并且无法正确调试它,就是这样。
有什么建议吗?
更新:调用函数的示例程序:
#include <iostream>
#include "string.hpp"
int main() {
exscape::string s("Hello, world!");
exscape::string s2; // Use the default constructor
s2 = s.reverse();
std::cout << "Reversed: " << s2 << std::endl;
return 0;
}
(gdb) break exscape::string::reverse
Breakpoint 1 at 0x402ed6: file string.cpp, line 368.
(gdb) run
Starting program: /home/serenity/programming/cpp/string/a.out
Breakpoint 1, exscape::string::reverse (this=0x7fffffffdc80) at string.cpp:368
368 string rev;
(gdb) n
378 rev.alloc(this->_length + 1);
(gdb) p rev
$1 = {buf = 0x7fffffffdca0 "", _length = 140737488346240, _size = 140737488346208}
(gdb) finish
Run till exit from #0 exscape::string::reverse (this=0x7fffffffdc80) at string.cpp:378
0x000000000040163f in main () at bare.cpp:6
6 s2 = s.reverse();
Value returned is $2 = {buf = 0x607030 "!dlrow ,olleH", _length = 13, _size = 14}
(gdb) n
7 std::cout << "Reversed: " << s2 << std::endl;
(gdb) n
Reversed: !dlrow ,olleH
更新
我将代码复制到我的Mac上,并尝试在那里进行调试 - 像魅力一样工作:
Breakpoint 1, exscape::string::reverse (this=0x7fff5fbff800) at string.cpp:368
368 string rev;
(gdb) n
378 rev.alloc(this->_length + 1);
(gdb) p rev
$1 = (exscape::string &) @0x7fff5fbff7c0: {
buf = 0x0,
_length = 0,
_size = 0
}
那个版本的gdb非常古老 - “GNU gdb 6.3.50-20050815(Apple version gdb-1344)”。 我尝试将Linux gdb降级到6.8和6.6无济于事。 - 我实际上也尝试了6.3,也没有用(并且完全不相关的说明,看来这些年来标签的完成逐渐变得越来越好了: - )。
答案 0 :(得分:2)
我认为问题可能是rev是返回值?
我不知道原因,但试试这段代码: 如果您使用Test * p =&amp; res;
并在gdb中打印&amp; res和p,您会看到两个地址不同,即使您使用-O0
进行编译也是如此#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
struct Test {
int a;
int b;
int c;
Test() : a(0), b(0), c(0) {}
Test operator+(const Test &rhs) const;
};
Test Test::operator+(const Test &rhs) const
{
Test res, nores;
// cannot print the content of res (uninitialized), but nores is right.
res.a = a + rhs.a;
res.b = b + rhs.b;
res.c = c + rhs.c;
return res;
}
ostream &operator<<(ostream &out, const Test &a)
{
out << a.a << ' ' << a.b << ' ' << a.c << endl;
}
int main()
{
Test p1, p2;
cout << p1 + p2 << endl;
return 0;
}
答案 1 :(得分:0)
我的猜测是,编译器不会创建局部变量'rev',直到它被引用(非常正常的优化),并且'rev.alloc'是对本地的第一个引用,直到它才会被创建该代码已被执行。
这就是调试器在调用'rev.alloc'之前显示未初始化数据的原因。我相信如果您切换到装配步进,您将看到如果您可以解释装配。或者你可以在调用'rev.alloc'之前“使用”'rev',在这种情况下,我很确定当你在'rev.alloc'中断时,你会看到初始化的本地。