【问题标题】:Segfault in custom string class自定义字符串类中的段错误
【发布时间】:2014-09-22 01:42:56
【问题描述】:

所以我正在尝试完成这个非常基本的字符串类(MyString)。一切似乎都正常,但是当我将其上传到作业站点时,它显示了一个段错误。上传网站使用了电围栏,但并没有提供太多关于故障发生位置的信息。它基本上贯穿每个函数并为其返回通过/失败/错误。对于getline 函数,它返回了一个错误。

另外,上传网站使用 valgrind 没有报告错误。

编辑:我差点忘了,当我在驱动程序中调用该函数时,它从文件messages.txt 中读取,其中包含一行文本:Testing this program... PLEASE WORK

下面是getline 函数(因为它存在于实现文件中),它似乎是错误的根源:

// reads line from istream ... line end at newline char of choice) -- '\n' in this case
void MyString::getline(istream &inFile, char delimit)
{
    int index = 0;
    do
    {
        data[index] = inFile.get();
        index ++;
        if (index + 1 > capacity)
        {
            MyString tempStr;
            delete [] tempStr.data;
            tempStr.data = new char [capacity];
            for (int i = 0; i <= index; i++)
            {
                tempStr.data[i] = data[i];
            }
            capacity += 5;
            size = index;
            delete [] data;
            data = new char [capacity];
            for (int i = 0; i <= size; i++)
            {
                data[i] = tempStr.data[i];
            }
            delete [] tempStr.data;
            tempStr.data = NULL;
        }
    }
    while (!inFile.eof() && data[index-1] != delimit);
   if (data[index - 1] == delimit)
    {
        index -= 1;
        if (static_cast<double>(index)/capacity < .25 && capacity > 5)
        {
            capacity -= 5;
            char *temp = new char [capacity];
            for (int i = 0; i < index; i++)
            {
                temp[i] = data[i];
            }
            delete [] data;
            data = temp;
        }
    }
    data[index] = '\0';
    size = index + 1;
}

我觉得要么是我忽略了一些非常简单的事情,要么是我处理这个特定功能的方式中的一个根本缺陷。任何帮助表示赞赏。我对编程非常陌生(几周后),我只是想维持生计——同时注册了 CompSci 1 + 2。

此外,下面是更多的实现文件——特别是构造函数(减去副本)和一些重载的运算符。虽然我可以最终编译它并成功连接类对象,但上传站点在测试“连接”时返回失败。没有任何关于哪个操作员失败的反馈。我很好奇我的代码中可能导致这种情况的原因。再次感谢。

#include <iostream>
#include <fstream>
#include "MyString.h"

using namespace std;

//default constructor - works
MyString::MyString()
{
    capacity = 5;
    size = 0;
    data = new char [capacity];
}

// constructor with character string
MyString::MyString(const char *cString) 
{
    int index = 0;
    capacity = 5;
    while ( cString[index] != '\0')
    {
        index++;
    }
    size = index + 1;
    while (size > capacity)
    {
        capacity += 5;
    }
    data = new char[capacity];
    for (int i = 0; i < size; i++)
    {
        data[i] = cString[i];
    }
}

// copy constructor
MyString::MyString(const MyString &aMyString)
{
    capacity = aMyString.capacity;
    size = aMyString.size;
    data = new char [capacity];
    for (int i = 0; i < size; i++)
    {
        data[i] = aMyString.data[i];
    }
}
// overloaded += operator
void MyString::operator+=(const MyString &aMyString)
{
    int tSize1 = size;
    int holder = 0;
    size += aMyString.size - 1;
    while (size > capacity)
    {
        capacity += 5;
    }
    char *tempArr = new char [capacity];
    for (int i = 0; i < (tSize1 - 1); i ++)
    {
        tempArr[i] = data[i];
    }
    for (int i = (tSize1 - 1); i < size; i++)
    {
        tempArr[i] = aMyString.data[holder];
        holder ++;
    }
    delete [] data;
    data = tempArr;
}

// overloaded + operator
MyString MyString::operator+(const MyString &aMyString) const
{
    int holder = 0;
    MyString tempS;
    int tSize1 = size + aMyString.size - 1;
    int tCap1 = capacity + aMyString.capacity;
    if (static_cast<double>(tSize1)/tCap1 < .25 && tCap1 > 5)
    {
        tCap1 -= 5;
    }
    tempS.size = tSize1;
    tempS.capacity = tCap1;
    delete [] tempS.data;
    tempS.data = new char [tempS.capacity];
    for (int i = 0; i < (size - 1); i ++)
    {
        tempS.data[i] = data[i];
    }
    for (int i = (size - 1); i < tSize1; i++)
    {
        tempS.data[i] = aMyString.data[holder];
        holder ++;
    }
    return tempS;
}

【问题讨论】:

  • 那是很多代码。你为什么不跟踪并找出它在哪里出现了段错误,然后问我们为什么......你可以使用调试器(gdb、Visual Studio 等)进行跟踪,或者使用 printf 添加跟踪消息。
  • 你还没有定义复制构造函数。
  • 这可能不会导致您的段错误,但您的代码有 while (!eof) 反模式的变体。您需要检查流是否在读取后失败但之前对提取的值进行任何操作。
  • 您使用什么开发环境进行编程? (操作系统/编译器等)
  • 我目前正在使用代码块(如大学推荐的那样)并在 Windows 7 中这样做。

标签: c++ string segmentation-fault getline dynamic-arrays


【解决方案1】:

我不知道这些是否都是你的错误,但我发现有两个从代码中脱颖而出:

for (int i = 0; i <= index; i++)
{
    tempStr.data[i] = data[i];
}
[snip]
for (int i = 0; i <= size; i++)
{
     data[i] = tempStr.data[i];
}

两个for 语句都访问了比它们应该访问的字符多一个。如果您想处理某事 5 次,例如在基于零的索引中,请检查 i &lt; 5 而不是 i &lt;= 5 。如果我没记错的话,你在这两个 for 循环中都犯了这个错误。我认为他们应该是:

for (int i = 0; i < index; i++)

for (int i = 0; i < size; i++)

在数组边缘之外写入内存可能会导致诸如段错误之类的问题。

【讨论】:

  • 这是官方的:我是个白痴。当我重新执行 getline 函数时,我以为我已经改变了这一点。我错了。感谢您的帮助 :D :D。我可能会在不知不觉中盯着这个看太多小时。
  • 有时以全新的眼光看待问题可以揭示我们在其他情况下可能看不到的事情。如果这确实解决了您的问题,我将不胜感激您接受答案。
  • 当然可以^^。如果您对为什么连接函数似乎对我有效,但对其他无效有任何见解,我将非常感激 - 但无论如何不用担心。
  • 就是这样:当我编译它时,它工作得很好,但服务器只是输出:“测试连接......失败。”没有关于失败原因或测试方式的反馈。
  • 是的...我尝试了许多变体来试图查明问题。显然不能聊天,因为我的代表太少了。我想我只有一个问题,之后我会回到将我的代码扔到服务器上直到它工作的苦恼:在“operator+=”或“operator+”函数中是否有任何突出的错误? -容易吗?
【解决方案2】:

马上,问题出在您的operator +=。它应该返回对对象的引用,而不是 void

第二,operator +应该写成operator +=。相反,您“从头开始”编写了整个 operator +,复制了 operator +=

中的代码

下面是operator+的实现方式:

// overloaded + operator
MyString MyString::operator+(const MyString &aMyString)
{
    MyString result = *this;   // copy the object
    result += aMyString;   // call the operator += (where the real work is done).
    return result;   // just return the result 
}

你的 operator+=,需要这样定义:

MyString& MyString::operator+(const MyString &aMyString)
{
   // code to do work
   return *this;
}

第三,你没有实现赋值运算符。您实现了复制构造函数,但没有实现赋值运算符。除非您没有发布它,否则如果您想将一个字符串分配给另一个字符串,则必须实现它。

最后,您的operator+= 有一个缺陷。它在这里改变了size 的值:

size += aMyString.size - 1;

然后尝试分配内存。如果内存分配失败(new 抛出异常)怎么办?您如何将size 的值“回滚”到其原始值?你不能,至少你的实现不能。

总而言之,仅仅因为您的实现“工作”并不意味着它真的工作正常。我在上面指出的事情(没有赋值运算符,运算符 += 不返回引用,*this)只是两个问题。

这样的任务的问题在于,它会给你一种错误的成就感,事实上,你编写的代码有你从未意识到的错误,但更糟糕的是,很容易创建。例如:

int main()
{
  MyString s("abc");
  MyString t("123");
  s = t;
}

如果没有赋值运算符,该代码会由于内存泄漏和双重删除错误而失败。

确实需要一个中级到高级程序员才能创建是的,听起来很简单的“字符串”类,至少可以通过所有测试,使字符串类在实际程序中可用。

【讨论】:

  • 很好奇。该分配特别要求“operator+=”返回 void。这也是我们在课堂上介绍它的方式。这个想法是它实际上改变了第一个对象的值。鉴于我未能发布重载赋值运算符,这可能不清楚。我在实现中确实有一个,只是不确定原始帖子中是否需要它。感谢您的洞察力。我知道前面还有很长的路要走,所以问题^^。
  • @uber08 - 即使返回 void,您仍然可以使用 operator += 来实现 operator +。要点是您应该利用已经编写的代码。
猜你喜欢
  • 2021-11-26
  • 2019-06-02
  • 1970-01-01
  • 2020-11-12
  • 2019-02-23
  • 2013-04-07
  • 2011-02-20
  • 1970-01-01
  • 2019-01-19
相关资源
最近更新 更多