我不断收到分段错误,但不知道是什么原因造成的
I keep getting a segmentation error and have no idea what's causing it?
当我运行这个程序str.cpp + str.h + main时,我不断收到分段错误(核心转储.cpp我一生都无法找出导致它的原因。我尝试遍历每一行代码并替换变量,但仍然收到该错误。这是我的代码,我要添加到我们的教授提供给我们的文件中:
//str.cpp
const str& str::operator=(const str& s)
{
str a;
a._n = s.length();
a._buf = new char[a._n];
strcpy(a._buf, s._buf);
return s;
};
str operator+(const str& a, const str& b)
{
str c ;
c._n = a._n + b._n;
c._buf = new char[c._n];
strcpy(c._buf,a._buf);
strcat(c._buf ,b._buf);
//strcat(strcat(c._buf,""),b._buf);
return c;
}
//main.cpp
#include "str.h"
int main() {
str s1;
cout << s1 << endl;
str s2("Hello");
cout << s2 << endl;
str s3("World");
str s4 = s2 + " " + s3;
cout << s4 << endl;
str s5, s6;
cin >> s5 >> s6;
cout << s5 << ' ' << s6;
return 0;
}
输出:./str <-- 用户
你好
世界您好
123 345 <-- 用户
分段故障(核心转储)
我希望输出为: ./str <-- 用户
你好
世界您好
123 345 <-- 用户
123 345
如果有人能帮忙,那就太好了。我真的不知道还能改变/做什么
问题operator=
实现中出错的核心是它不分配给目标对象。让我们分解一下会发生什么:
const str& str::operator=(const str& s)
{
str a; // make a temporary
a._n = s.length(); // assign source's length to temporary's length
a._buf = new char[a._n]; // assign a buffer to the temporary
strcpy(a._buf, s._buf); // assign source's data to temporary
return s; // return source
}; // temporary goes out of scope and is destroyed
由于所有东西都存储在临时a
而不是this
中,因此dest = source;
dest
之后保持不变。当a
在功能结束时超出范围并被销毁时,所有完成的工作都丢失了。
要解决此问题:
源的返回有点奇怪,但是一旦其余部分修复,您可能永远不会注意到效果。按照习惯,您应该返回刚刚分配的对象。关于operator=
(以及所有常见的运算符重载)应该是什么样子的真正很好的阅读可以在这里找到:运算符重载的基本规则和习语是什么?这既简单又容易摆脱困境,因此我们将在进入主要事件之前进行第一次更正。
str& str::operator=(const str& s)
{
str a;
a._n = s.length();
a._buf = new char[a._n];
strcpy(a._buf, s._buf);
return *this;
};
请注意,此函数的内涵看起来与 sbi 建议的完全不同。 sbi 对赋值运算符使用复制和交换方法。我们稍后会谈到这一点,因为它通常是一个很好的起点,几乎同样经常是一个停下来的好地方,因为它是如此简单,几乎不可能出错。
创建一个临时的a
,会为您提供除预期目的地以外的其他内容,this
,以分配给您,因此让我们摆脱a
并使用this
。
const str& str::operator=(const str& s)
{
this->_n = s.length();
this->_buf = new char[this->_n];
strcpy(this->_buf, s._buf);
return *this;
};
但是我们不需要在任何地方都明确声明this
,所以
const str& str::operator=(const str& s)
{
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf);
return *this;
};
现在,目标对象已被分配了源中所有内容的副本,但是如果目标中已经有内容怎么办?哎 呦。只是泄露了它。假设nullptr
空str
或提供了有效的分配,则此问题的解决方案是快速的:
const str& str::operator=(const str& s)
{
delete[] _buf;
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf);
return *this;
};
但是,如果您正在做一些像a = a
这样的愚蠢事情并将对象分配给自己怎么办?
const str& str::operator=(const str& s)
{
delete[] _buf; // same object so this deleted s._buf
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf); // and s._buf is gone, replaced by the new empty _buf
// bad stuff will happen here
return *this;
};
你可以简单地说,"那是你的事,傻瓜。C++标准库中有很多这样的想法,所以你不会出格,但如果你想宽容
const str& str::operator=(const str& s)
{
if (this != &s)
{
delete[] _buf;
_n = s.length();
_buf = new char[_n];
strcpy(_buf, s._buf);
}
return *this;
};
现在我们有一些不会让我们尴尬的东西。除非s.length()
不包含允许strcpy
正常运行的终止 null。传统上,字符串长度函数不包括终止 null,因此我们需要一个额外的字符来适应 null 终止符。顺便说一下,这是崩溃的最可能原因。
const str& str::operator=(const str& s)
{
if (this != &s)
{
delete[] _buf;
_n = s.length();
_buf = new char[_n + 1]; // +1 for null terminator
strcpy(_buf, s._buf);
}
return *this;
};
请注意,在为_buf
分配缓冲区的所有位置都需要这个额外的字节。
回到复制和交换,有点。
你需要一个复制构造函数来实现复制和交换的复制部分,但要遵守三法则,对象无论如何都需要有一个复制构造函数。让我们来看看我们需要什么才能实现一个。
str::str(const str& s)
{
if (this != &s) // don't need because you have to be really stupid to copy yourself
{
delete[] _buf; // don't need because this is a new object. It can't have an
// existing allocation
_n = s.length();
_buf = new char[_n + 1];
strcpy(_buf, s._buf);
}
};
有可能
str a(a);
并构造一个对象作为其不完全构造的自我的副本。我真的不知道为什么这在C++是合法的。可能是因为防止它的规则会搞砸其他更重要的事情。这样做有点史诗般的愚蠢。a = a;
可能发生,因为一些令人困惑的间接寻址,您实际编写的内容是a = b;
,但是一些函数b
被初始化为对a
的引用。或者,也许您正在使用指针并搞砸了。无论如何,它可能发生。str a(a);
需要一些深思熟虑或错别字。无论哪种方式,它都是错误的,应该被修复。我不建议尝试防止这种情况。
无论如何,我们可以将复制构造函数简化为
str::str(const str& s)
{
_n = s.length();
_buf = new char[_n + 1];
strcpy(_buf, s._buf);
};
通过删除我们不需要的东西。然后,通过成员初始值设定项列表的魔力,我们可以将构造函数简化为
str::str(const str& s): _n(s._n), _buf(new char[_n + 1])
{
strcpy(_buf, s._buf);
};
有了这种美感,您就可以利用复制和交换
str& str::operator=(str s) // copy constructor does all the copying
{
std::swap(_n, s._n); // exchange guts with s
std::swap(_buf, s._buf);
return *this;
}; // destruction of s deletes this's old _buf
或者这个,如果你已经实现了自己的swap
函数来交换str
s。
str& str::operator=(str s)
{
swap(*this, s);
return *this;
};
如果您稍后要对str
进行排序,那么拥有一个swap
函数非常方便。
噗!现在让我们看看operator+
str operator+(const str& a, const str& b)
{
str c ;
c._n = a._n + b._n;
c._buf = new char[c._n]; // need a +1 here for the null terminator
strcpy(c._buf,a._buf);
strcat(c._buf ,b._buf);
//strcat(strcat(c._buf,""),b._buf);
return c;
}
这家伙还不错。需要一些额外的空间来放置终结器(c._buf = new char[c._n+1];
),但其他方面可以维修。你可能可以停在这里。
但是,如果您还需要为作业实现operator+=
,则不妨使用惯用的解决方案。运算符重载的基本规则和习语是什么?建议实施operator+=
并围绕它进行operator+
。那看起来像
str& str::operator+=(const str& b)
{
_n += b._n;
char * temp = new char[_n+1]; // can't use _buf yet. Need free it's memory and
// to copy from it
strcpy(temp, _buf);
strcat(temp, b._buf);
delete[] _buf; // now we can release and reuse _buf
_buf = temp;
return *this;
}
str operator+(str a, const str& b)
{
a+=b;
return a;
}
关于成语的快速说明:
一般来说,除非有令人信服的理由不这样做,否则应该坚持惯用的解决方案。经验丰富的程序员可以立即识别它们,不需要解释。这样可以节省代码审查和编写文档的时间,并在其他人处理或使用您的代码时减少意外。最重要的是他们工作。它们可能不如专门的解决方案工作,例如,复制和交换习惯用法的开销可能不必要地昂贵,但它们是一个很好的起点,直到证明有更好的解决方案并且你有充分的理由利用其他解决方案。
你需要改变
a._buf = new char[a._n];
自
a._buf = new char[a._n + 1];
如果没有str
的定义,就不可能确切地说出需要什么,但有一件事是肯定的,你不能返回对局部变量的引用。需要这样的东西...
const str& str::operator=(const str& s)
{
this->_n = s.length();
this->buf = new char[s._n]; // Plus one?
strcpy(this->buf, s._buf);
return *this;
};
编辑
这是为了 Jive 的好处 - 因为注释中的代码效果不太好
str s1("Wibble"); // s1 has memory
str &s2 = s1; // s2 is a reference to s1
s1 = s2;
- 努力将整数转换为链表。不知道我在这里做错了什么
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 我不知道这条线是做什么的
- 如果 KEY 是 std::list 或 std::vector 而不是值,那么 std::map 的默认行为是什么?
- 是什么导致了这种使用三进制而不是短整型的有符号int到无符号int转换
- 是否总是可以将使用递归编写的程序重写为不使用递归的程序C++,性能观点是什么?
- B不接受8作为输入的是什么?C++
- 必须具有泛型接口的函数,但必须根据传递的子类(不知道它们是什么!)以不同的行为 - C++
- *(flow + i*n + j) 在 C 中是什么意思?我不知道这个话题叫什么
- 我不知道这是什么意思 (x^y) ?在 C++ 中
- 当我打印字符串时,我不知道它是什么输出
- 我不断收到分段错误,但不知道是什么原因造成的
- C++98 中获取并继续调用当前类不知道的类方法的最简单方法是什么?
- 测试使用visual studio编写的C++android应用程序的最佳方法是什么.不升级到windows专业版
- 视觉错误类 c++ 类。 不知道它是什么
- 我不知道是什么导致了我的代码中的分段错误
- 我怎么知道是什么使不完整类型不完整的错误:对指向不完整类型的指针进行算术运算
- 奇怪的循环情况,不知道是什么原因造成的
- 我不知道这个错误是什么意思
- 堆栈损坏,不知道是什么原因造成的