Custom string implementation, operator + overload memory allocation issue - c++

I am trying to create custom string implementation in c++ and I have a problem with overloading operator "+". I want to append new string to existing one and when I try to allocate a new extended string, debugger allocates around 12 bytes more then it should. I have no control over allocation, he just ignores variable "length". Here is the code:
class String
{
private:
char *ptrNiz;
public:
String(const char *niz = "")
{
ptrNiz = new char[strlen(niz)+1];
strcpy_s(ptrNiz, strlen(niz)+1, niz);
cout << "stvoren niz: " << ptrNiz << endl;
}
String(const String& refString)
{
ptrNiz = new char[strlen(refString.ptrNiz) + 1];
strcpy_s(ptrNiz, strlen(refString.ptrNiz) + 1, refString.ptrNiz);
cout << "Kopiran niz: " << ptrNiz << endl;
}
~String()
{
cout << "Unisten objekt: " << ptrNiz << endl;
delete[] ptrNiz;
}
int Length() const
{
return strlen(ptrNiz);
}
int CharAt(int i) const
{
return ptrNiz[i];
}
void Ispisi()
{
cout << ptrNiz << endl;
}
operator char*()
{
return ptrNiz;
}
String operator+=(const const String& ref)
{
const int const length = ref.Length() + this->Length() + 1;
char *temp = new char[length]; // ignores length and allocates more space
for (int i = 0; i < this->Length(); i++)
{
temp[i] = ptrNiz[i];
}
for (int j = 0; j < ref.Length(); j++)
{
temp[this->Length() + j] = ref.ptrNiz[j];
}
return String(temp);
}
};

char *temp = new char[length]; // ignores length and allocates more space
All which your C++ implementation is required to do here is returning a pointer to a memory location with room for length bytes. Nothing more, nothing less. It is allowed to internally allocate more bytes, even though your code must not try to access them (or else the behaviour is undefined).
The low-level details of dynamic memory allocation is an implementation issue, not your code's concern.
A debug build is a very good example for a situation in which a C++ implementation may use extra bytes for debugging information. In fact, that's probably what is happening here. I suppose you are using Visual C++. As its documentation (CRT Debug Heap Details) says:
When you request a memory block, the debug heap manager allocates from
the base heap a slightly larger block of memory than requested and
returns a pointer to your portion of that block.
[...]
The additional memory allocated by the debug heap routines is used for
bookkeeping information, for pointers that link debug memory blocks
together, and for small buffers on either side of your data to catch
overwrites of the allocated region.
Note that there are very many other grave errors in your code. I decided to ignore them for now and just answered the exact question you asked.

You are allocating twice, once in the constructor and again in the operator+= method. You also aren't deleting the pointer in the operator function so that memory is leaking. Either use smart pointers or provide an additional constructor parameter which specifies the ownership of the incoming pointer.

In your code please add the following line in to terminate the string with NULL;
String operator+=(const const String& ref)
{
// You code goes here
temp[length - 1] = '\0';
return String(temp);
}
This will work as expected.
Hope this helps.

Related

C++ Spell checking program with two classes; Dictionary and word

Here is the specification for the code:
You are to use the Word and Dictionary classes defined below and write all member functions and any necessary supporting functions to achieve the specified result.
The Word class should dynamically allocate memory for each word to be stored in the dictionary.
The Dictionary class should contain an array of pointers to Word. Memory for this array must be dynamically allocated. You will have to read the words in from the file. Since you do not know the "word" file size, you do not know how large to allocate the array of pointers. You are to let this grow dynamically as you read the file in. Start with an array size of 8, When that array is filled, double the array size, copy the original 8 words to the new array and continue.
You can assume the "word" file is sorted, so your Dictionary::find() function must contain a binary search algorithm. You might want to save this requirement for later - until you get the rest of your program running.
Make sure you store words in the dictionary as lower case and that you convert the input text to the same case - that way your Dictionary::find() function will successfully find "Four" even though it is stored as "four" in your Dictionary.
Here is my code so far.
#include <cstring>
#include <iostream>
#include <fstream>
using namespace std;
class Word
{
char* word_;
public:
Word(const char* text = 0);
~Word() { delete[] word_; word_ = nullptr; }
const char* word() const;
};
Word::Word(const char* arg)
: word_(new char[strlen(arg) + 1])
{
strcpy(word_, arg);
}
const char* Word::word() const
{
return word_;
}
class Dictionary
{
Word** words_;
unsigned int capacity_; // max number of words Dictionary can hold
unsigned int numberOfWordsInDictionary_;
void resize() {
capacity_ = capacity_ * 2;
cout << "Size = " << capacity_ << endl;
};
void addWordToDictionary(char* word) { words_ += *word; };
public:
Dictionary(const char* filename);
~Dictionary() {
delete[] words_; words_ = nullptr;
};
bool find(const char* word);
};
Dictionary::Dictionary(const char * filename)
: words_(new Word*[8]), capacity_(8), numberOfWordsInDictionary_(0)
{
ifstream fin(filename);
if (!filename) {
cout << "Failed to open file!" << endl;
}
char buffer[32];
while (fin.getline(buffer, sizeof(buffer)))
{
if (numberOfWordsInDictionary_ == capacity_)
{
resize();
}
addWordToDictionary(buffer);
}
}
bool Dictionary::find(const char * left)
{
int last = capacity_ - 1,
first = 0,
middle;
bool found = false;
while (!found && first <= last) {
middle = (first + last) / 2;
if (strcmp(left, reinterpret_cast<char*>(words_[middle])) == 0) {
found = true;
}
else if (left > reinterpret_cast<char*>(words_[middle]))
last = middle - 1;
else
first = middle + 1;
}
return found;
}
;
bool cleanupWord(char x[] ) {
bool lower = false;
int i = 0;
while (x[i]) {
char c = x[i];
putchar(tolower(c));
lower = true;
}
return lower;
}
int main()
{
char buffer[32];
Dictionary Websters("words.txt");
ifstream fin("gettysburg.txt");
cout << "\nSpell checking " << "gettysburg.text" << "\n\n";
while (fin >> buffer) {
if (cleanupWord(buffer) == true) {
if (!Websters.find(buffer)) {
cout << buffer << " not found in the Dictionary\n";
}
}
}
system("PAUSE");
}
When I run the program it stops after outputting "spellchecking Gettysburg.txt" and I don't know why. Thank you!
The most likely cause of this problem is the text files have not been opened. Add a check with is_open to make sure they have been opened.
When using Relative Paths (any path that does not go all the way back to the root of the file system (and is an Absolute Path)), take care that the program is being run from the directory you believe it to be. It is not always the same directory as the executable. Search Term to use to learn more about this: Working Directory.
Now on to other reasons this program will not work:
void addWordToDictionary(char* word) { words_ += *word; };
is not adding words to the dictionary. Instead it is advancing the address at which words_ points by the numeric value of the letter at *word. This is extremely destructive as it loses the pointer to the buffer allocated for words_ in the constructor making delete[] words_; in the Dictionary destructor ineffective and probably fatal.
Instead you want to (Note I use want to with a bit of trepidation. What you really want to do is use std::vector and std::string, but I strongly suspect this would upset the assignment's marker)
Dynamically allocate a new Word with new.
Place this word in a free spot in the words_ array. Something along the lines of words_[numberOfWordsInDictionary_] = myNewWord;
Increase numberOfWordsInDictionary_ by 1.
Note that the Words allocated with new must all be released in the Dictionary destructor. You will want a for loop to help with this.
In addition, I would move the
if (numberOfWordsInDictionary_ == capacity_)
{
resize();
}
from Dictionary to addWordToDictionary so that any time addWordToDictionary is called it is properly sized.
Hmmm. While we're at it, let's look at resize
void resize() {
capacity_ = capacity_ * 2;
cout << "Size = " << capacity_ << endl;
};
This increases the object's capacity_ but does nothing to allocate more storage for words_. This needs to be corrected. You must:
Double the value of capacity_. You already have this.
Allocate a larger buffer to hold the replacement of words_ with new.
Copy all of the Words in words_ to the larger buffer.
Free the buffer currently pointed to by words_
Point words_ at the new, larger buffer.
Addendum
I haven't looked closely at find because the carnage required to fix the reading and storage of the dictionary will most likely render find unusable even if it does currently work. The use of reinterpret_cast<char*> is an alarm bell, though. There should be no reason for a cast, let alone the most permissive of them all, in a find function. Rule of thumb: When you see a reinterpret_cast and you don't know what it's for, assume it's hiding a bug and approach it with caution and suspicion.
In addition to investigating the Rule of Three mentioned in the comments, look into the Rule of Five. This will allow you to make a much simpler, and probably more efficient, dictionary based around Word* words_, where words_ will point to an array of Word directly instead of pointers to Words.

Deleting a character pointer getting heap error

The following code is supposed to implement my own string class. Similar to if you were to create something like this String s = "Hi";. I am getting an error when it goes to destroy and gets to the part where delete[] data. Is says I am writing when I am out of the heap buffer. These are not cstrings so there is not a null character at the end of my string.
Here is my converting/default constructor:
String346::String346(const char * oldString) : data(NULL), size(static_cast<unsigned int>(strlen(oldString))){
data = new(std::nothrow) char[size];
for (unsigned int i = 0; i <= getSize(); i++){
data[i] = oldString[i];
}
}
Since these functions need to support function chaining I am going to put my both functions that relate to my problem one where if a String346 object was passed or if a char * was passed in.
Concatenating function where char * is passed in:
String346 & String346::concat(const char * catString) {
String346 newCatString(catString);
concat(newCatString);
return (*this);
}
Concatenating function where String346 object is passed in:
String346 & String346::concat(const String346 & catString) {
String346 tempData(data);
size = tempData.getSize() + catString.getSize();
destroy();
data = new (std::nothrow) char[size];
if (data == NULL){
std::cout << "Not enough space to concatinate this string." << std::endl;
}
else{
unsigned int index = 0;
for (unsigned int i = 0; i < getSize(); i++){
if (i < tempData.getSize()){
data[i] = tempData.data[i];
}
else{
data[i] = catString.data[index];
index++;
}
}
}
return (*this);
}
My destroy function which does all the work for the destruction of an object is simple. It contains these three lines:
delete[] data;
data = NULL;
size = 0;
return;
Your constructor allocates a char array containing size elements.
Then, your constructor appears to copy size+1 characters to the array (I am assuming that getSize() returns size).
Therefore, the constructor code runs off the end of the array, and corrupts one byte past the end of the allocated array.
P.S. The static_cast is not needed, and only makes the code more obfuscated.
The first line in your concat method:
String346 tempData(data);
passes a char * to your constructor which is not null terminated, so the call to strlen will go past the end of the string.
The next two lines also don't work:
size = tempData.getSize() + catString.getSize();
destroy();
destroy sets size back to zero, which means the rest of your method will not do anything.
You should try running this through a debugger and single step through it - you can then check the values of your variables at each step and make sure your program is doing what you expect.
Also, if you have a member variable that is freed in the destructor, you should look into the "rule of three" or "rule of five", to make sure things don't get freed twice.

Why one of those two similar functions crashes and one works ok?

This is really weird. If I increase the value of asize just by one crashSystem() does what its name speaks. Second function returning an int pointer works ok with much bigger values. Those two functions just delete and allocate the same dynamic array with same size (I created it just for test purposes).
Note: I think it could have something to do with maximum stack capacity 1MB (130037 * 8 in bytes is near 1MB), but it's really strange 'cause allocating using new inside function should work the same as any other new.
Using Visual Studio 2015
#include <iostream>
void crashSystem(int * dynamicArray, int asize) {
delete[] dynamicArray;
//dynamicArray = nullptr; does not matter at all
dynamicArray = new int[asize];
std::cout << "mem allocated\n";
}
int * worksOk(int * dynamicArray, int asize) {
int * newDynamicArray = new int[asize];
delete[] dynamicArray;
std::cout << "mem allocated\n";
return newDynamicArray;
}
int main()
{
int asize = 130037; // dynamic array size
//asize = 12330037; // for testing second function that works
int * dynamicArray;
dynamicArray = new int[asize];
for (int i = 0; i < 100; i++)
{
std::cout << "iteration " << i << " ";
crashSystem(dynamicArray, asize);
//dynamicArray = worksOk(dynamicArray, asize);
}
std::cout << "\n";
system("PAUSE");
}
Note 2: Crashing app this way in Release mode tends to block executable by creating non existent process (checked with Process Hacker 2)
The problem is that you're passing pointer by value, so it still points to the new int[asize]; allocated in main(), on which you then call multiple delete [].
It becomes a dangling pointer after the first delete [] call.
Even assigning nullptr won't help you if the pointer is not being passed by reference.
worksOk works, because you're returning the pointer pointing to the newly allocated space and assigning it, so it's valid every time.
Just change the signature so it uses reference:
void crashSystem(int *&dynamicArray, int asize)
and it should work fine.

SIGSEGV when dynamically allocating memory to receive FTP server's LIST response

I am building an FTP client in C++ for personal use and for the learning experience, but I have run into a problem when allocating memory for storing LIST responses. The library I am using for FTP requests is libcurl which will call the following function when it receives a response from the server:
size_t FTP_getList( char *ptr, size_t size, size_t nmemb, void *userdata) {
//GLOBAL_FRAGMENT is global
//libcurl will split the resulting list into smaller approx 2000 character
//strings to pass into this function so I compensate by storing the leftover
//fragment in a global variable.
size_t fraglen = 0;
if(GLOBAL_FRAGMENT!=NULL) {
fraglen = strlen(GLOBAL_FRAGMENT);
}
size_t listlen = size*nmemb+fraglen+1;
std::cout<<"Size="<<size<<" nmemb="<<nmemb;
char *list = new char[listlen];
if(GLOBAL_FRAGMENT!=NULL) {
snprintf(list,listlen,"%s%s",GLOBAL_FRAGMENT,ptr);
} else {
strncpy(list,ptr,listlen);
}
list[listlen]=0;
size_t packetSize = strlen(list);
std::cout<<list;
bool isComplete = false;
//Check to see if the last line is complete (i.e. newline terminated)
if(list[size]=='\n') {
isComplete = true;
}
if(GLOBAL_FRAGMENT!=NULL) {
delete[] GLOBAL_FRAGMENT;
}
GLOBAL_FRAGMENT = GLOBAL_FTP->listParse(list,isComplete);
delete[] list;
//We return the length of the new string to prove to libcurl we
//our function properly executed
return size*nmemb;
}
The function above calls the next function to split each line returned into individual
strings to be further processed:
char* FTP::listParse(char* list, bool isComplete) {
//std::cout << list;
//We split the list into seperate lines to deal with independently
char* line = strtok(list,"\n");
int count = 0;
while(line!=NULL) {
count++;
line = strtok(NULL,"\n");
}
//std::cout << "List Count: " << count << "\n";
int curPosition = 0;
for(int i = 0; i < count-1 ; i++) {
//std::cout << "Iteration: " << i << "\n";
curPosition = curPosition + lineParse((char*)&(list[curPosition])) + 1;
}
if(isComplete) {
lineParse((char*)&(list[curPosition]));
return NULL;
} else {
int fraglen = strlen((char*)&(list[curPosition]));
char* frag = new char[fraglen+1];
strcpy(frag,(char*)&(list[curPosition]));
frag[fraglen] = 0;
return frag;
}
}
The function above then calls the function below to split the individual entries in a line into separate tokens:
int FTP::lineParse(char *line) {
int result = strlen(line);
char* value = strtok(line, " ");
while(value!=NULL) {
//std::cout << value << "\n";
value = strtok(NULL, " ");
}
return result;
}
This program works for relatively small list responses but when I tried stress testing it by getting a listing for a remote directory with ~10,000 files in it, my program threw a SIGSEGV... I used backtrace in gdb and found that the segfault happens on lines delete[] GLOBAL_FRAGMENT;' anddelete[] list;inFTP_getList. Am I not properly deleting these arrays? I am callingdelete[]` exactly once for each time I allocate them so I don't see why it wouldn't be allocating memory correctly...
On a side note: Is it necessary to check to see if an array is NULL before you try to delete it?
Also, I know this would be easier to do with STD::Strings but I am trying to learn c style strings as practice, and the fact that it is crashing is a perfect example of why I need practice, I will also be changing the code to store these in a dynamically allocated buffer that only is reallocated when the new ptr size is larger than the previous length, but I want to figure out why the current code isn't working first. :-) Any help would be appreciated.
In this code
size_t listlen = size*nmemb+fraglen+1;
std::cout<<"Size="<<size<<" nmemb="<<nmemb;
char *list = new char[listlen];
if(GLOBAL_FRAGMENT!=NULL) {
snprintf(list,listlen,"%s%s",GLOBAL_FRAGMENT,ptr);
} else {
strncpy(list,ptr,listlen);
}
list[listlen]=0;
You are overruning your list buffer. You have allocated listlen bytes, but you write a 0 value one past the last allocated byte. This invokes undefined behavior. More practically speaking, it can cause heap corruption, which can cause the kind of errors you observed.
I didn't see any issues with the way you are calling delete[].
It is perfectly safe to delete a NULL pointer.

STL container leak

I'm using a vector container to hold instances of an object which contain 3 ints and 2 std::strings, this is created on the stack and populated from a function in another class but running the app through deleaker shows that the std::strings from the object are all leaked. Here's the code:
// Populator function:
void PopulatorClass::populate(std::vector<MyClass>& list) {
// m_MainList contains a list of pointers to the master objects
for( std::vector<MyClass*>::iterator it = m_MainList.begin(); it != m_MainList.end(); it++ ) {
list.push_back(**it);
}
}
// Class definition
class MyClass {
private:
std::string m_Name;
std::string m_Description;
int m_nType;
int m_nCategory;
int m_nSubCategory;
};
// Code causing the problem:
std::vector<MyClass> list;
PopulatorClass.populate(list);
When this is run through deleaker the leaked memory is in the allocator for the std::string classes.
I'm using Visual Studio 2010 (CRT).
Is there anything special I need to do to make the strings delete properly when unwinding the stack and deleting the vector?
Thanks,
J
May be Memory leak with std::vector<std::string> or something like this.
Every time you got a problem with the STL implementation doing something strange or wrong like a memory leak, try this :
Reproduce the most basic example of what you try to achieve. If it runs without a leak, then the problem is in the way you fill the data. It's the most probable source of problem (I mean your own code).
Not tested simple on-the-fly example for your specific problem :
#include <string>
#include <sstream>
// Class definition
struct MyClass { // struct for convenience
std::string m_Name;
std::string m_Description;
int m_nType;
int m_nCategory;
int m_nSubCategory;
};
// Prototype of populator function:
void populate(std::vector<MyClass>& list)
{
const int MAX_TYPE_IDX = 4;
const int MAX_CATEGORY_IDX = 8;
const int MAX_SUB_CATEGORY_IDX = 6;
for( int type_idx = 0; type_idx < MAX_TYPE_IDX ; ++type_idx)
for( int category_idx = 0; category_idx < MAX_CATEGORY_IDX ; ++category_idx)
for( int sub_category_idx = 0; sub_category_idx < MAX_SUB_CATEGORY_IDX ; ++sub_category_idx)
{
std::stringstream name_stream;
name_stream << "object_" << type_idx << "_" << category_idx << "_" << sub_category_idx ;
std::stringstream desc_stream;
desc_stream << "This is an object of the type N°" << type_idx << ".\n";
desc_stream << "It is of category N°" << category_idx << ",\n";
desc_stream << "and of sub-category N°" << category_idx << "!\n";
MyClass object;
object.m_Name = name_stream.str();
object.m_Description = desc_stream.str();
object.m_nType = type_idx;
m_nCategory =
m_nSubCategory =
list.push_back( object );
}
}
int main()
{
// Code causing the problem:
std::vector<MyClass> list;
populate(list);
// memory leak check?
return 0;
}
If you still got the memory leak, first check that it's not a false-positive from your leak detection software.
Then if it's not, google for memory leak problems with your STL implementation (most of the time on the compiler developer website). The implementor might provide a bug tracking tool where you could search in for the same problem and potential solution.
If you still can't find the source of the leak, maybe try to build your project with a different compiler (if you can) and see if it have the same effect. Again if the leak still occurs, the problem have a lot of chances to come from your code.
Probably same root issue as Alexey's link. The shipped version has broken move code for basic_string. MS abandoned us VC10 users, so you must fix it yourself. in xstring file you have this:
_Myt& assign(_Myt&& _Right)
{ // assign by moving _Right
if (this == &_Right)
;
else if (get_allocator() != _Right.get_allocator()
&& this->_BUF_SIZE <= _Right._Myres)
*this = _Right;
else
{ // not same, clear this and steal from _Right
_Tidy(true);
if (_Right._Myres < this->_BUF_SIZE)
_Traits::move(this->_Bx._Buf, _Right._Bx._Buf,
_Right._Mysize + 1);
else
{ // copy pointer
this->_Bx._Ptr = _Right._Bx._Ptr;
_Right._Bx._Ptr = 0;
}
this->_Mysize = _Right._Mysize;
this->_Myres = _Right._Myres;
_Right._Mysize = 0;
_Right._Myres = 0;
}
return (*this);
}
Note the last
_Right._Myres = 0;
that should happen only under the last condition, for the short case _Right should better be left alone.
As the capacity is set to 0 instead of 15, other code will take unintended branch in function Grow() when you assign another small string and will allocate a block of memory just to trample over the pointer with the immediate string content.