Segfault in custom string class - c++

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

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.

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.

Related

C++ char class function append

void CMPT135_String::append(const CMPT135_String &s) /*12*/
{
char *temp = new char[this->getLength()+1];
int len = CMPT135_String::cstrlen(buffer);
for (int i =0; i < len; i++)
temp[i] = this->operator[](i);
for (int i = 0; i < s.getLength()+1; i++)
temp[len+i] = s[i];
if(this->getLength() >0)
delete[] this->buffer;
this->buffer = temp;
}
I have been working with this append() member function for a custom string class.
The function works fine, but after it runs I get a popup window showing up:
Windows has triggered a breakpoint in CMPT135_Assignment1.exe.
This may be due to a corruption of the heap, which indicates a bug in Assignment1.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while CMPT135_Assignment1.exe has focus.
The output window may have more diagnostic information.
Please tell me what is going wrong with this.
You are not allocating enough memory for the new buffer. You are only allocating enough memory to copy this->getLength() number of characters from this, there is no room to also copy s.getLength() number of characters from s into that new buffer, so your second loop is corrupting random memory past the end of the new buffer.
Try something more like this instead:
void CMPT135_String::append(const CMPT135_String &s) /*12*/
{
int this_len = this->getLength();
int s_len = s.getLength();
int new_len = this_len + s_len;
char *temp = new char[new_len + 1];
for (int i = 0; i < this_len; ++i)
temp[i] = this->operator[](i);
for (int i = 0; i < s_len; i++)
temp[this_len + i] = s[i];
temp[new_len] = '\0';
delete[] this->buffer;
this->buffer = temp;
}

How do I prevent this buffer overrun?

I'm writing a class which deals with polynomials of an arbitrary size, and when overloading the * operator I have a buffer overrun warning that I don't know how to deal with.
Polynomial Polynomial::operator*(const Polynomial& rhs)
{
int returnSize = (this->size + rhs.getSize()) - 1;
int* returnArray = new int[returnSize];
for (int i = 0; i < this->size; i++)
{
for (int j = 0; j < rhs.getSize(); j++)
{
returnArray[i + j] = this->polynomial[i] * rhs.polynomial[j];
}
}
}
And I think that by the nature of the program that it isn't actually possible to get buffer overrun, but I don't understand it very well so I'm probably mistaken. The warning states:
C6386: Buffer Overrun whilst writing to 'returnArray': the writable size is 'returnSize*4' bytes, but'8' bytes might be written.
I'm also having this issue with the copy constructor.
Polynomial::Polynomial(const Polynomial& rhs)
{
this->size = rhs.getSize();
this->polynomial = new int[rhs.getSize()];
for (int i = 0; i < rhs.getSize(); i++)
{
this->polynomial[i] = rhs.polynomial[i];
}
}
I really hope this isn't a daft question, and thanks for your help!
The warning is caused because the compiler doesn't know the value of returnSize at compile time, and assumes it may be less than 2. You can easily remove this warning by assigning space for at least two int values in your new call:
int* returnArray = new int[std::max(returnSize,2)]; // Need #include <algorithm> for std::max

C++. How can I free memory correctly?

Written code to find and remove the largest word in a string without the using of library functions. Everything works fine. But when I want to free memory, the result is negative (displays an empty line). If you remove the call to the memory release function, everything will work correctly, but there will be a leak of memory.
How do I fix it? Please help me.
#include <iostream>
using namespace std;
int length(char *text) // string length
{
char *begin = text;
while(*text++);
return text - begin - 1;
}
int size(char **text) // size of two-dimensional array
{
int i = 0;
while(text[i]) i++;
return i;
}
void free_memory(char **text)
{
for(int i=0; i<size(text); i++)
delete text[i];
delete [] text;
}
char **split(char *text, char delim)
{
int words = 1;
int len = length(text);
for(int i=0; i<len; i++)
if(text[i] == delim) words++;
char **result = new char*[words + 1];
int j = 0, t = 0;
for(int i=0; i<words; i++)
{
result[i] = new char[len];
while(text[j] != delim && text[j] != '\0') result[i][t++] = text[j++];
j++;
t = 0;
}
result[words + 1] = nullptr;
return result;
}
char *strcat(char *source, char *destination)
{
char *begin = destination;
while(*destination) destination++;
*destination++ = ' ';
while(*source) *destination++ = *source++;
return begin;
}
char *removeWord(char *in_string)
{
char **words = split(in_string, ' ');
int max = length(words[0]);
int j = 0;
for(int i=0; i<size(words); i++)
if(max < length(words[i]))
{
max = length(words[i]);
j = i;
}
int index;
char *result;
if(!j) index = 1;
else index = 0;
result = words[index];
for(int i=0; i<size(words); i++)
if(i != j && i != index)
result = strcat(words[i], result);
free_memory(words); // I want free memory here
return result;
}
int main()
{
char text[] = "audi and volkswagen are the best car";
cout << removeWord(text) << endl;
return 0;
}
In fact, this is C style programming - not C++. I see that your aim is to implement everything from scratch, possibly for practicing. But even then, your code is not designed/structured properly.
Besides that, you also have several bugs in your code:
result[words + 1] = nullptr; must be result[words] = nullptr;
You need result[i][t] = '\0'; after the while loop in split
delete text[i] must be delete [] text[i]
You cannot assign to your result pointer memory from words, then free it and then return it for use by the caller.
There is at least one further bug in the second half of removeWord. It would be tedious to try to understand what you are trying to do there.
You might want to start with a simpler task. You also should proceed step-by-step and check each function for correctness independently first and not implement everything and then test. Also take a look at the tool valgrind for memory checking - if you use Linux.
The way you free memory correctly is to use RAII:
Only use new and new[] in constructors
Pair those with delete and delete[] in the corresponding destructor
Use automatic storage duration objects as much as possible
If you are specifically not using std::string and std::vector etc, for reasons of learning pointers, you will end up writing some small number of classes that resemble string and vector and unique_ptr, and then you go about programming as if you were using the std versions.
You have two issues. First is that result is assigned to a memory location in words. Second, is that you're storing the result of strcat in words[i] which will likely not have enough room (see strcat documentation).
result = new char[len(in_string)+1]; // +1 for space for null char
// the old loop reversed the word order -- if you want to keep doing
// that, make this a descending loop
for(int i=0; i<size(words); i++)
if(i != j && i != index)
strcat(result, words[i]);
free_memory(words);
return result;
So that when you free words, what result points to is also free'd. You would then need to free your result in main().
int main()
{
char text[] = "audi and volkswagen are the best car";
char * result = removeWord(text);
cout << result << endl;
delete[] result;
return 0;
}

C++ Having issues reusing the same object to hold a different value

Recently, I've been trying to understand why this error occurs:
BigNum num = 50;
num = 2;
When I create a BigNum with a value of 50, then try to input a value of 2 into it later, my program crashes.
These are the two constructors I use:
// constructor where operand is a long
BigNum::BigNum(long num) {
negative = num < 0;
unsigned long absNum = abs(num);
length = numberOfDigits(absNum);
data = new char[length];
for (int i = 0; absNum != 0; i++) {
data[i] = absNum % 10;
absNum /= 10;
}
}
// copy constructor
BigNum::BigNum(BigNum const& orig) {
int arrSize = orig.length;
data = new char[arrSize];
copy(orig.data, orig.data + arrSize, data);
negative = orig.negative;
length = orig.length;
}
// destructor
BigNum::~BigNum() {
delete[] data;
}
The elements in the object are:
bool negative;
int length;
char *data;
of which data holds digits for the BigNum.
However, the use of both of these constructors and destructor does not allow me to refactor the value of the object.
Could anyone shed some light on why this would be happening?
Thank you very much for your time.

C++ returning locally allocated dynamic memory without leak?

I am learning about C++ in school and creating a string class for an assignment. I have a couple of questions:
First, will the following create a memory leak?
MyString operator() (int sliceStart, int sliceEnd) {
sliceStart = sliceStart%_len;
if(sliceStart < 0)
sliceStart = _len + sliceStart;
sliceEnd = sliceEnd%_len;
if(sliceEnd < 0)
sliceEnd = _len + sliceEnd;
char* temp = new char[_len + 1];
if(sliceStart == sliceEnd)
return *this;
int i;
if(sliceStart < sliceEnd)
for(i = 0; sliceStart < sliceEnd; ++sliceStart && ++i)
temp[i] = _str[sliceStart];
else if(sliceStart > sliceEnd) {
for(i = 0; sliceStart < _len; ++sliceStart && ++i)
temp[i] = _str[sliceStart];
for(int k = 0; k < sliceEnd; ++i && ++k)
temp[i] = _str[k];
}
temp[i] = '\0';
//delete [] temp
return MyString(temp);
}
And if so, will adding the commented delete line return a valid value since it is freed right before the return. Or is it possible that the memory could possibly be snatched up between the two lines?
(I know that in this particular case I could simply create a MyString object before the return, but I am curious as if I HAD to return the temp char)
What is the common/proper way to manage dynamic local memory that you also wish to return by value?
Yes - there's a memory leak in your code, you allocate memory and then potentially return a different value on the next line.
Use encapsulation to help the user understand the lifetime of any allocations; either use something like std::shared_ptr or create a class that wraps ownership of the allocation.