0

So I am trying to complete this very basic string class (MyString). Everything seemed to work, but when I uploaded it to the assignment site, it showed a segfault. The upload site uses electric fence, but it didn't give much insight as to where the fault occurred. It essentially runs through each function and returns a pass/fail/fault for it. In the case of the getline function, it returned a fault.

Also, the upload site uses valgrind which reported no errors.

EDIT: I almost forgot, when I called the function in the driver, it read from a file messages.txt, which contained one line of text: Testing this program... PLEASE WORK

Below is the getline function (as it exists in the implementation file) that appears to be the source of the fault:

// 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;
}

I feel like it's either something very simple I overlooked or a fundamental flaw in the way I approached this particular function. Any help is appreciated. I'm very new to programming (few weeks in) and am just trying to stay afloat -- got enrolled in CompSci 1 + 2 simultaneously.

Additionally, below is more of the implementation file -- particularly, the constructors (minus copy) and a few overloaded operators. While I could compile it on my end and concatenate class objects successfully, the upload site returned a fail when it tested "Concatenation." There wasn't any feedback as to which operator failed. I was curious what might cause that in my code. Thanks again.

#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;
}
uber08
  • 3
  • 4
  • 1
    that's a lot of code. why don't you trace and figure out which where it segfaults, then ask us why... you can trace with a debugger (gdb, visual studio, etc.) or, alternatively, add trace messages with printf. – thang Sep 22 '14 at 01:56
  • You haven't defined copy constructor. – timrau Sep 22 '14 at 01:58
  • This may not be causing your segfault, but your code has a variant of the [`while (!eof)`](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) antipattern. You need to check if the stream has failed after reading but **before** doing anything with the extracted value. – user657267 Sep 22 '14 at 02:10
  • What development environment do you use to program in? (OS/Compiler etc) – Michael Petch Sep 22 '14 at 02:33
  • I'm currently using codeblocks (as recommended by the university) and doing so in Windows 7. – uber08 Sep 22 '14 at 02:39
  • So that would be codeblocks with MinGW C/C++ compiler. Thanks. One other thing, I see you mention that on their server valgrind reports no errors. I am surprised at that given what I think I see as a couple bugs. I wonder if they have their valgrind set up correctly. – Michael Petch Sep 22 '14 at 02:42
  • It's possible that they don't I was surprised too given that there were errors in the getline function. In any event, the accepted answer solved the segfault issue, but I'm still not sure as to why the concatenation test fails on the server but works for me. – uber08 Sep 22 '14 at 02:56

2 Answers2

0

I don't know if these are all your bugs but I see two that stand out from the code:

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];
}

Both the for statements are accessing one more character than they should. If you want to process something 5 times for example in zero based indexing you check for i < 5 not i <= 5 . If I am not mistaken you have made that error in both these for loops. I believe they should be:

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

and

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

Writing memory beyond the edge of your arrays can cause problems like the segfaults.

Michael Petch
  • 42,023
  • 8
  • 87
  • 158
  • It's official: I'm an idiot. I thought I had changed that when I went through and re-did the getline function. I was wrong. Thanks for your help :D :D. I probably would have stared at this for way too many hours without realizing it. – uber08 Sep 22 '14 at 02:42
  • Sometimes a fresh set of eyes on a problem can shed light on things we might not see otherwise. If this does solve your problem I'd appreciate you accepting the answer. – Michael Petch Sep 22 '14 at 02:43
  • Sure does ^^. If you have any insight as to why the concatenation functions seems to work on my end but not another, I'd be much obliged -- but no worries in any case. – uber08 Sep 22 '14 at 02:54
  • That's just the thing: when I compile it, it works just fine, but the server just outputs: "Testing Concatenation ... FAILED." There's no feedback as to what failed or how it's being tested. – uber08 Sep 22 '14 at 03:00
  • Yeah... I've tried a number of variations to try to pinpoint the issue. Apparently can't chat because I have too little rep. I guess I just have one question after which I'll just go back to the grind of throwing my code against the server until it works: is there anything that stands out in either the "operator+=" or "operator+" functions as being error-prone? – uber08 Sep 22 '14 at 03:11
  • They will be testing fringe cases. Something that may fail is if you try to concatenate a MyString object (or both of them) where at least one is not initialized with a string. I think this might fail `MyString a; MyString b("test"); MyString c = a + b` – Michael Petch Sep 22 '14 at 03:27
  • That's a good point. I've been focusing too much on getting objects initialized or assigned to some set of values. That'll probably get me where I need to go. If I could give you rep for each comment, I would. Thanks for all your help. – uber08 Sep 22 '14 at 03:56
0

Right away, the problem is your operator +=. It's supposed to return a reference to the object, not void.

Second, operator + should be written in terms of operator +=. Instead you wrote the whole operator + "from scratch", duplicating the code in operator +=

Here is how operator+ should be implemented:

// 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 
}

Your operator+=, needs to be defined as this:

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

Third, you failed to implement an assignment operator. You implemented the copy constructor, but not the assignment operator. Unless you didn't post it, it has to be implemented if you want to assign one string to another.

Last, your operator+= has a flaw. It changes the value of size here:

size += aMyString.size - 1;

Then attempts to allocate memory. What if that memory allocation fails (new throws an exception)? How do you "rollback" the value of size to its original value? You can't, at least not with your implementation.

To summarize, just because you have your implementation "working" doesn't mean it really is working properly. The things I pointed out above (no assignment operator, and operator += not returning a reference, *this) are just two issues.

The problem with assignments like this is that it can give you the false sense of accomplishment, when truthfully, what you have coded has bugs you never realized, but worse, can be easily created. For example:

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

Without an assignment operator, that code fails due to a memory leak and a double deletion error.

It really takes an intermediate to advanced programmer to create yes, something that sounds simple as a "String" class properly, at least one that can pass all the tests that would make a String class usable in a real program.

PaulMcKenzie
  • 31,493
  • 4
  • 19
  • 38
  • Curious. The assignment specifically calls for the "operator+=" to return void. That's how we covered it in class as well. The idea being that it's actually changing the value of the first object. That might be unclear given my failure to post the overloaded assignment operator. I do have one in the implementation, just wasn't sure if it was needed in the original post. Thanks for the insight though. I know there's a long road ahead, hence the questions ^^. – uber08 Sep 22 '14 at 04:30
  • @uber08 - You can still implement operator + by using operator +=, even if returning void. The main point is that you should take advantage of code that is already written. – PaulMcKenzie Sep 22 '14 at 10:00