C++ String and Pointers Manipulation - c++

Suppose I have this data structure in C++ :
struct Stash {
int size; // Size of each space
int quantity; // Number of storage spaces
int next; // Next empty space
// Dynamically allocated array of bytes:
unsigned char* storage;
// Functions!
void initialize(int size);
void cleanup();
int add(const void* element);
void* fetch(int index);
int count();
void inflate(int increase);
};///:~
void Stash::initialize(int sz) {
size = sz;
quantity = 0;
storage = 0;
next = 0;
}
int Stash::add(const void* element) {
if(next >= quantity) // Enough space left?
inflate(increment);
// Copy element into storage,
// starting at next empty space:
int startBytes = next * size;
unsigned char* e = (unsigned char*)element;
for(int i = 0; i < size; i++){
storage[(startBytes + i)] = e[i];
}
next++;
return(next - 1); // Index number
}
void* Stash::fetch(int index) {
// Check index boundaries:
assert(0 <= index);
if(index >= next)
return 0; // To indicate the end
// Produce pointer to desired element:
int value = (index*size);
return &(storage[value]);
}
int Stash::count() {
return next; // Number of elements in CStash
}
void Stash::inflate(int increase) {
assert(increase > 0);
int newQuantity = quantity + increase;
int newBytes = newQuantity * size;
int oldBytes = quantity * size;
unsigned char* b = new unsigned char[newBytes];
for(int i = 0; i < oldBytes; i++)
b[i] = storage[i]; // Copy old to new
delete []storage; // Old storage
storage = b; // Point to new memory
quantity = newQuantity;
}
void Stash::cleanup() {
if(storage != 0) {
cout << "freeing storage" << endl;
delete []storage;
}
} ///:~
Suppose now I use the data structure to memorize strings in this way :
int main(){
Stash* st1 = new Stash;
st1->initialize(sizeof(string));
string s1 = "This is a GOOD morning";
st1->add(&s1);
string s2 = "This is a BAD morning";
st1->add(&s2);
string* s3;
s3 = static_cast<string*> (st1->fetch(0));
cout << *s3 << endl;
string* s3;
s3 = static_cast<string*> (st1->fetch(1));
cout << *s3 << endl;
st1->cleanup();
delete st1;
return 0;
}
It Works!!! This is the output:
This is a GOOD morning
This is a BAD morning
But in this other way:
int main(){
Stash* st1 = new Stash;
st1->initialize(sizeof(string));
string s1 = "This is a GOOD morning";
st1->add(&s1);
s1 = "This is a BAD morning";
st1->add(&s1);
string* s3;
s3 = static_cast<string*> (st1->fetch(0));
cout << *s3 << endl;
string* s4;
s4 = static_cast<string*> (st1->fetch(1));
cout << *s4 << endl;
st1->cleanup();
delete st1;
return 0;
}
It doesn't work. This is the output:
This is a BAD morning
This is a BAD morning
So, what happened in the machine when I try to use the same reference?
I have tried with other datatypes and it works well.

The first use of s1 invokes the constructor:
string s1 = "This is a GOOD morning";
You then add the address of s1 to the Stash. Next you assign a new value to s1:
s1 = "This is a BAD morning";
This doesn't create a new string -- it invokes the assignment operator which replaces the same string object with a new value. You then save another copy of the address of s1:
st1->add(&s1);
If you look at the data in st1 then you'll see you have two copies of the same pointer, both pointing to s1. This is expected. In the first case you are storing pointers to two different objects which contain different values.

Your code copies the bytes making up a std::string container (NOT the characters that are in the string data). This probably consists of a pointer to the string data, plus a size and capacity.
When you write s1 = "stuff", the std::string internally allocates new memory, so its previous internal pointer is now invalid.
Then you retrieve that previous internal pointer from your data structure and try to use it, causing undefined behaviour.
If your intent is to save the characters in the string then you need to add s1.c_str() instead of &s1.
If your intent is to store a copy of any object then you need to invoke the copy constructor to create a copy; not do a bitwise copy as you are currently doing. You could also invoke a move constructor or move assignment operator if your intent is to store the object and not leave the original object behind.

In your second approach, you have used address of s1 to be stored in stack. And you are not copying the contents within the stack, so when you change s1 to have a different content earlier content also changes, because you essentially pushed the pointer not the copy of the content.
For example, if you do the following (copying the content to a new string to be used for push), this works:
string s1 = "This is a GOOD morning";
st1->add(new string(s1));
s1 = "This is a BAD morning";
st1->add(new string(s1));

Related

Create Dynamically Allocated Array of Pointers C++

I'm currently trying to dynamically allocate an array of character arrays and set values from another array of character arrays to the new dynamically array. When I print the values from the dynamically array I got some junk values and I can not understand where they come from.
Class -
class Class {
private:
char** courses;
int numberOfCourses;
public:
Class();
Class(const char** courses, int numberOfCourses);
~Class();
char** getCoursesList();
int getNumberOfCourses();
};
Constructor (allocate memory) -
Class:: Class(const char **courses, int numberOfCourses) {
if (numberOfCourses <= 0){
this->numberOfCourses = 0;
this->courses = nullptr;
} else{
this->numberOfCourses = numberOfCourses;
this->courses = new char*[numberOfCourses];
for (int i = 0; i < numberOfCourses; i++) {
cout << strlen(courses[i]) << endl; // 5
this->courses[i] = new char[strlen(courses[i])];
cout << strlen(this->courses[i]) << endl; // 22
strncpy(this->courses[i], courses[i], strlen(courses[i]));
}
}
}
getNumberOfCourses -
int Class::getNumberOfCourses() {
return this->numberOfCourses;
}
getCoursesList -
char **Class::getCoursesList() {
return this->courses;
}
Main -
const char *courses[] = {"test1", "test2", "test3" };
Class d1(courses,3);
for (int i = 0; i < d1.getNumberOfCourses(); i++) {
cout << d1.getCoursesList()[i] << endl;
}
Output -
[test1═²²²²▌▌▌▌▌▌l┴╓K▌] [test2═²²²²▌▌▌▌▌▌#┴2K▌] [test3═²²²²▌▌▌▌▌▌Y┴;K▌]
I would love to understand what I am doing wrong.
Look here as you may understand from the documentation, strlen function does not count \0 character which is end of the string. Hence it is not copied with strcpy function call, and cout does not encounter with \0. This is the reason of absurd characters in terminal output. While allocating memory for course names, allocate for one more char and add \0 end of the char array.

C++ struct values lost

I have a strange issue. I allocate char[] values in struct array, but they get lost:
------- The struct is this one :
typedef struct _Settings
{
const char* str;
uint val;
}Settings;
------- I create it like this :
int nn=10;
settings = new Settings[nn];
for (int i = 0; i < nn; i++) {
string strr = "thisOneIs";
strr.append(std::to_string(i));
settings[i].str = strr.c_str();
string teststr = settings[i].str; //// (1)
settings[i].val = i + 1;
}
..... at (1), I get the correct values.
But if I then call this (same place, right after the code above), the settings[i].str is empty:
for (int i = 0; i < nn; i++) {
string teststr = settings[i].str; ///// (2)
std::cout << settings[i].str << "=" << settings[i].val << "\n";
}
... at (2), I get empty.
Does anyone have a clue why? Thanks!
The line at (1) is a problem because you are storing a pointer to some memory that is not valid when the loop ends.
string strr = "thisOneIs"; // A temporary object in the loop.
strr.append(std::to_string(i));
settings[i].str = strr.c_str(); // Pointer that won't be valid when the loop ends.
If you learning about low level language features, it's ok to experiment with using char* and raw memory. If you are trying to get a working program, just use std::string.
Also simplify the definition of Settings. You don't need all the typedef non-sense in C++.
struct Settings
{
std::string str;
uint val;
};

Creating a personal string vector class

I am not allowed to make use of the vector class so I need to make my own. I made a int vector class and it works fine, but when trying to make it for strings it compiles but gives me an error because of the pointers. Any hint where I am making the mistake? All I did was change every int element for string, but aparently that does not work. Please help I am very confused.
public:
StringRow(){
elements = new string;
size = 0;
}
~StringRow(){...}
void push_back(string value){...}
};
You defined pointer to variable, not array of variables.
elements = new string;
Replace it with
elements = new string[size];
You can optimize algorithm with defining initial size. Create bigger array only if it's necessary.
There are several problems:
in the constructor you don't need to allocate anything. You don't even need a constructor here, you can initialize the members directly as you declare them.
if you allocate with string* tmpElementsArray = new string[size + 1]; you need to deallocate with delete [] tmpElementsArray;
Corrected working version:
#include <string>
#include <iostream>
using namespace std;
class StringRow {
private:
string* elements = nullptr;
int size = 0;
public:
// constructor not needed
// StringRow() {
// elements = nullptr;
// size = 0;
// }
~StringRow() {
delete []elements;
}
void push_back(string value) {
string* tmpElementsArray = new string[size + 1];
for (int i = 0; i<size; i++) {
tmpElementsArray[i] = elements[i];
}
delete [] elements;
elements = tmpElementsArray;
elements[size] = value;
size++;
}
int length() {
return size;
}
string at(int index) {
if (index<size) {
return elements[index];
}
}
};
int main()
{
StringRow s;
string str1 = "hello";
string str2 = "hello2";
s.push_back(str1);
s.push_back(str2);
cout << s.at(0) << endl ;
cout << s.at(1) << endl;
}
Doing a delete []elements if elements is nullptr is OK.
NB: This is not the most efficient way.

How can I prevent these memory leaks?

I met huge problem with memory leaks and I don't know where to put that "delete" to get rid of them. Below is part of my code, and there is a full one: https://pastebin.com/Wtk83nuH.
string* startowa(int& rozmiar)
{
rozmiar = 5;
string* tablica = new string[rozmiar];
for (int i = 0; i < rozmiar; i++)
tablica[i] = "text";
return tablica;
}
string* plusx(string* tab, int& rozmiar)
{
string tekst = "something";
string* tablica_3 = new string[rozmiar];
tablica_3[rozmiar - 1] = tekst;
for (int i = 0; i<rozmiar - 1; i++)
tablica_3[i] = tab[i];
return tablica_3;
}
string* minusx(string* tab, int& rozmiar)
{
string* tablica_3 = new string[rozmiar];
for (int i = 0; i < rozmiar; i++)
tablica_3[i] = tab[i];
return tablica_3;
}
int main()
{
int wybor = 1, rozmiar = 1;
string *tablica = startowa(rozmiar);
while (wybor != 55) {
cin >> wybor;
if (wybor == 1) {
rozmiar++;
tablica = plusx(tablica, rozmiar);
}
if (wybor == 6) wybor = 55;
else {
rozmiar--;
tablica = minusx(tablica, rozmiar);
}
// there were other "ifs" but its just a part of the code
}
for (int i = 0; i < rozmiar; i++)
cout << tablica[i] << endl;
delete[] tablica;
cin >> wybor;
getchar();
return 0;
}
The memory leak is your least problem in that source code. In fact, you don't need heap allocations at all in your example.
Here are some fast improvements:
- use "std::string" instead of just string, I guess you are using "using namespace std"
- do not return a pointer to string, you can just declare a string and return it
- do not use a reference to an int as a function parameter if you are not returning it
- use const as much as you can
- replace "string *" with "const string&" if you are not returning it
- do not allocate string on heap (with new), instead declare it on stack
- use vectors
You can use this great site and Scott Meyers books for other C++ good practices.
To prevent memory leaks like that, avoid manual memory management. There are a lot of tools available to you.
For example, take your string array:
string* startowa(int& rozmiar) {
rozmiar = 5;
string* tablica = new string[rozmiar];
// ...
}
This should be replaced by std::vector. And since a vector keep track of it's size, you don't need to pass the size as reference:
std::vector<std::string> startowa() {
// ...
std::vector<std::string> tablica(5);
// ...
}
Then, your function that operates on the array should take the vector by reference to about copies, and return another vector. Since a vector already has a function that insert a new element, your plusx function becomes this:
void plusx(std::vector<std::string>& tab) {
std::string tekst = "something";
tab.emplace_back(std::move(tekst));
}
And your minusx function becomes that:
void minusx(std::vector<std::string>& tab) {
tab.pop_back();
}
By the way, with a vector, you can completely remove your startowa function by replacing the call in your main by this:
// Was `string *tablica = startowa(rozmiar);`
std::vector<std::string> tablica(5, "text");
Since std::vector manages it's memory itself, you don't need to delete it anywhere.
If you don't want to use vector, you can alway use std::unique_ptr<std::string[]>. The only difference in you code would be to send tablica.get() to your functions, and use std::make_unique<std::string[]>(rozmiar) instead of new std::string[rozmiar]
The correct answer is use std::vector. For example:
vector<string> startowa(int& rozmiar)
{
rozmiar = 5;
vector<string> tablica(rozmiar);
for (int i = 0; i < rozmiar; i++)
tablica[i] = "text";
return tablica;
}
Note the return by value. Don't fall into the trap of thinking you're saving processing time by returning by reference. That vector goes out of scope and is destroyed at the end of the function. With a returned reference the best you can hope for is the caller receiving a load of garbage and crashing before any damage can be done.
A decent compiler will eliminate the copying when you return the vector by value, and if the compiler decides that it cannot, std::move will take care of that.
vector also knows how big it is, eliminating the need for rozmiar.
Now... What went wrong? Let's look at the code
int main()
{
int wybor = 1, rozmiar = 1;
string * tablica = startowa(rozmiar);
startowa allocated an array of strings and stored a pointer to the array in tablica.
while (wybor != 55)
{
cin >> wybor;
if (wybor == 1)
{
rozmiar++;
tablica = plusx(tablica, rozmiar);
plusx allocated a new array of strings, a pointer to which has been returned and written over the pointer returned by startowa. startowa's array is now effectively lost, leaked, as it is next to impossible to find again to delete[].
We would need to delete[] tablica; before making the assignment. Clearly we can't do this before calling plusx as tablica is a parameter, so we need to store a temp.
string * temp = plusx(tablica, rozmiar);
delete[] tablica;
tablica = temp;
But what if something unexpected happens and an exception is thrown? The code never hits the delete[] and BOTH allocations are lost. vector handles all this for you.
And back to the code
}
if (wybor == 6)
wybor = 55;
else
{
rozmiar--;
tablica = minusx(tablica, rozmiar);
Same problem and solution as above.
}
// there were other "ifs" but its just a part of the code
}
for (int i = 0; i < rozmiar; i++)
cout << tablica[i] << endl;
delete[] tablica;
One of an in-determinant number of allocations is released here. The rest are lost.
cin >> wybor;
getchar();
return 0;
}

Dynamic Memory Allocation for Dictionary

Hi there I need to Build something like a dictionary and each word according to my code can have 100 meanings, but maybe it has only 5 meanings then I will be allocating 95 extra space for nothing or maybe it has more than 100 meanings then the program will crash, I know the vector class is very easy and could be good use of, but the task is almost building my own vector class, to learn how it works. Thus **meanings and some other stuff remain the same and here is my code, Also I know I am causing memory leakage, how can I delete properly? :
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class Expression {
char *word_with_several_meanings; // like "bank", "class"
char **meanings; // a pointer to a pointer stores all meanings
int meanings_ctr; // meanings counter
//-----------FUNCTIONS------------------------------------------------
public:
void word( char* = NULL );
void add_meaning(char* = NULL);
char* get_word();
int get_total_number_of_meanings();
char* get_meaning(int meanx = 0);
Expression(int mctr = 0); // CTOR
~Expression(); // DTOR
};
Expression::Expression(int mctr ) {
meanings_ctr = mctr; // Setting the counter to 0
meanings = new char * [100]; // Allocate Space for 100 meanings
}
Expression::~Expression() {
delete [] meanings; // Deleting the memory we allocated
delete [] word_with_several_meanings; // Deleting the memory we allocated
}
void Expression::word( char *p2c )
{
word_with_several_meanings = new char[strlen(p2c)+1];
// copy the string, DEEP copy
strcpy(word_with_several_meanings, p2c);
}
void Expression::add_meaning(char *p2c)
{
//meanings = new char * [meanings_ctr+1];
meanings[meanings_ctr] = new char[strlen(p2c)+1];
strcpy(meanings[meanings_ctr++],p2c);
}
char * Expression::get_meaning( int meanx )
{
return *(meanings+meanx);
}
char * Expression::get_word()
{
return word_with_several_meanings;
}
int Expression::get_total_number_of_meanings()
{
return meanings_ctr;
}
int main(void) {
int i;
Expression expr;
expr.word("bank ");
expr.add_meaning("a place to get money from");
expr.add_meaning("b place to sit");
expr.add_meaning("4 letter word");
expr.add_meaning("Test meaning");
cout << expr.get_word() << endl;
for(int i = 0; i<expr.get_total_number_of_meanings(); i++)
cout << " " << expr.get_meaning(i) << endl;
Expression expr2;
expr2.word("class");
expr2.add_meaning("a school class");
expr2.add_meaning("a classification for a hotel");
expr2.add_meaning("Starts with C");
cout << expr2.get_word() << endl;
for( i = 0; i<expr2.get_total_number_of_meanings(); i++)
cout << " " << expr2.get_meaning(i) << endl;
Expression expr3;
expr3.word("A long test ... ");
char str[] = "Meaning_ ";
for (int kx=0;kx<26;kx++)
{
str[8] = (char) ('A'+kx);
expr3.add_meaning(str);
}
cout << expr3.get_word() << endl;
for(i = 0; i < expr3.get_total_number_of_meanings(); i++)
cout << " " << expr3.get_meaning(i) << endl;
return 0;
}
When you are allocating a multi dimensional array with new then you are allocating it with a loop, e.g.
char **x = new char*[size]
for (int i = 0; i < N; i++) {
x[i] = new int[size];
}
So you also have to delete it in this fashion:
for (int i = 0; i < N; i++) {
delete[] x[i];
}
delete[] x;
Thus when you're having arbitrary sizes of your array you'll have to store them somewhere for using them within the destructor.
delete [] meanings; // Deleting the memory we allocated
won't get rid of your memory allocated, only the pointers themselves.
To free up the actual memory, you will need to iterate through your meanings array, and delete [] each element in it.
Something like:
for (int i = 0; i < meanings_ctr; ++i)
{
delete [] meanings[meanings_ctr];
meanings[meanings_ctr] = NULL;
}
delete [] meanings;
--
For the problem of what to do if you get more than 100 meanings (or in general when your collection is full), the standard technique is to allocate a new array that is double the size (which you can do since it is dynamic), copy your existing collection into that one, and then dispose of your existing one.
I'd use a simple linked list (this is simplified, not complete and untested; also there should be proper getters/setters and stuff):
class Meaning {
char text[20];
Meaning *next;
Meaning(const char *text) : next(0) {
strcpy(this->text, text);
}
}
class Word {
char text[20];
Meaning *first;
Meaning *last;
Word(const char *text) : first(0), last(0) {
strcpy(this->text, text);
}
~Word() {
Meaning *m = first, *n;
while(m) {
n = m->next;
delete m;
m = n;
}
}
void AddMeaning(const char *text) {
if (last) {
last = last->next = new Meaning(text);
}
else {
first = last = new Meaning(text);
}
}
void print() {
printf("%s:\n\t", text);
Meaning *m = first;
while (m) {
printf("%s, ", m->text);
m = m->next;
}
}
}