Segmentation fault from calling a function from an object within an array - c++

I have a programming assignment that requires us to use a dynamically allocated two-dimensional char array in-lieu of strings and vectors. I have two classes: Word which holds a pointer to a char array, and WordList which holds a pointer to a Word array.
The segmentation fault comes from this section of code:
for(int i=0; i<listLength; i++)
fout << "Word " << i << (wordList[i])->getWord() << endl;
where fout is an ofstream object, wordList is a Word** object, and getWord() is a member function of a Word object. The thing is that I use the same wordList[i]->getWord() syntax in another member function of WordList and get the proper output.
Please let me know if more code is needed to properly diagnose the problem
More code:
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include "Word.h"
using namespace std;
class WordList
{
public:
int listLength_;
Word** wordList_;
WordList()
{
char blank = ' ';
char* blankPtr = &blank;
setListLength(1);
wordList_ = new Word* [listLength_];
for(int i=0; i<listLength_; i++)
{
wordList_[i] = new Word(blankPtr);
}
}
void addWord(Word* word, Word** wordList, int n)
{
Word** wl_temp = new Word* [n+1];
for(int i=0; i<n; i++)
{
wl_temp[i] = wordList[i];
}
wl_temp[n] = word;
delete[] wordList;
setWordList(wl_temp);
listLength_++;
cout << " " << (wordList_[n]->getWord()); //works here
}
void parse(const char* filename)
{
ifstream fin(filename);
char end;
char* tw;
while(fin >> end)
{
fin.unget();
fin.get(tw=new char[49], 49, ' ');
Word* w = new Word(tw);
addWord(w, getWordList(), getListLength());
delete w;
delete[] tw;
}
}
void output(const char* outfile)
{
ofstream fout(outfile);
for(int i=1; i<=listLength_; i++)
fout << "Word " << i << (wordList_[i])->getWord() << endl; //not here
fout.close();
}
};
int main(int argc, char* argv[])
{
WordList wordList;
wordList.parse(argv[1]);
wordList.output(argv[2]);
return 1;
}

In WordList::Wordlist:
wordList_[i] = new Word(blankPtr);
You're passing a pointer to a local variable here.
Not only is that a problem in itself, but the "string" isn't zero-terminated.
Regardless of whether Word assumed ownership of the object, this will cause undefined behaviour.
If Word::Word copies its argument, this is a very roundabout (and wrong) way to write new Word(" ").
In parse:
Word* w = new Word(tw);
addWord(w, getWordList(), getListLength());
delete w;
You added w to the word list. Now you're deleteing it.
The word list now contains a pointer to released memory.
Dereferencing it also causes undefined behaviour.
delete[] tw;
This is only OK if Word::Word copies its argument. Otherwise it now holds a pointer you aren't allowed to use for anything.
If you're going to work with hand-rolled allocation and raw pointers, you need to set a very clear policy for which object owns which memory and is responsible for allocating and releasing it.
The best time to do this is before you touch the keyboard.

Note that blankPtr in the constructor points to a local variable, this pointer will not be valid once the constructor returns. Also, in the parse function you delete the pointer to the string, making that pointer invalid as well. Not only that, you actually delete the Word object pointer, meaning you have now have an illegal pointer in your array.
Unless you create a copy (not just copy the pointer, but allocate new memory) inside the Word constructor, your Word objects will contain illegal pointers leading to undefined behavior.
Undefined behavior is tricky, since it can seem to work one time, but not another.

Related

Deletion of Dynamically Allocated Memory

I have been given a task, where I need to create the string_copy function Note that the function body and prototypes have been given by the source and that needs to be maintained. The portions written by me are after the comment write your code here.
#include <iostream>
using namespace std;
int string_length(const char* string_c);
char* string_copy(const char* string_c);
int main()
{
const char* string_c = "This is a string and is a long one so that we can create memory leaks when it is copied and not deleted";
// write your code here
int length = string_length(string_c);
cout << "Copied String: " << string_copy(string_c) << endl;
return 0;
}
int string_length(const char* string) {
int length = 0;
for (const char* ptr = string; *ptr != '\0'; ++ptr) {
++length;
}
return length;
}
char* string_copy(const char* string) {
// we need to add 1 because of ’\0’
char* result = new char[string_length(string) + 1];
// write your code here (remember zero-termination !)
int i;
for (i = 0; string[i] != '\0'; ++i)
{
result[i] = string[i];
}
result[i] = '\0';
return result;
}
Now task tells me
that it is very important that any memory allocated with e=new TYPE is
released later with delete e (and a=new TYPE[size] with delete [] a)
else this will lead to an error.
It is not exactly clear if error means compile/runtime error or error as in my task did not meet the requirement error.
My question is, in this code how do I delete the intermediate dynamically created result array? If I delete result, won't it fail the purpose of the task? Then how am I to respect the quotation above or maybe simulate memory leak as given in the long string constant?
Thanks.
EDIT: Why the negative votes? Please at least explain the reason! I am not asking any solution or something, but mere suggestion if I am missing some point or not!
The caller of string_copy would be responsible for releasing the memory when it's done with it by calling delete[] on it.
This is, by the way, a terrible way to write C++ code. You should be using std::string or std::vector<char> or something like that.
Here's why:
int length = string_length(string_c);
char* copy = string_copy(string_c);
cout << "Copied String: " << copy << endl;
delete[] copy;
return 0;
Yuck.
In fact the ideal solution is to use std::string and not char *. There is no real need of using char * instead of std::string in your example.
With std::string:
You don't need to new anything
You don't need to delete anything
You can do everything with std::string, that you do with char *.

Adding spaces at the beginning of an array of chars

As in the title, I need to add user-specified number of spaces at the beginning of some word, using array of chars. I need to do it in a function which takes my array as a parameter and returns it. Here's my code:
#include <iostream>
using namespace std;
void writeDownCharArray(char t[], int sizee)
{
for (int i=0;i<sizee;i++)
{
cout<<t[i];
}
}
char * addSpaces(char t[], int ammountOfSpaces)
{
int numberOfCharacters=0;
for (int i=0; t[i]!=NULL; i++){numberOfCharacters++;} //checking the amount of characters in my array
char t2[numberOfCharacters+10];
for (int i=0; i<ammountOfSpaces; i++) {t2[i]=' ';} //adding the sapces
for (int i=ilosc;i<numberOfCharacters+ammountOfSpaces;i++) {t2[i]=t[i-ammountOfSpaces];} //filling my new array with characters from the previous one
return t2;
}
int main()
{
int numberOfSpaces;
char t[10];
cout << "Text some word: ";
cin.getline(t,10);
cout<<"How many spaces?: ";cin>>numberOfSpaces;
writeDownCharArray(addSpaces(t, numberOfSpaces), HERE);
return 0;
}
And now: How do I print it to the screen? If I say cout<<addSpaces(t, numberOfSpaces); it actually prints something strange to the screen (not numbers, just strange characters). And if I say writeDownCharArray, then what should I put in "HERE" place?
The C++ way to solve this would be to use a std::string like
std::string add_spaces(const std::string & line, std::size_t number_of_spaces)
{
std::string spaces(number_of_spaces, ' ');
return spaces + line;
}
If you cannot use std::string then you are doing to have to deal with dynamic memory allocations and change
char t2[numberOfCharacters+10];
to
char * ts = new char[numberOfCharacters + ammountOfSpaces + 1];
We have to have this as Variable length arrays are not standard and trying to return a pointer to an array declared in a function will leave you with a dangling pointer and trying to use it is undefined behavior.
Since new[] was used in the function you will need to remember to call delete[] on the pointer that is returned after you are done with it. This is another benefit of using a std::string as it takes care of itself.
As far as writeDownCharArray is concerned you do not need a size parameter as cout can handle null terminated c-strings. You can simply have
void writeDownCharArray(char t[])
{
cout<<t;
}
And then you main would look like
char * foo = addSpaces(t, numberOfSpaces);
writeDownCharArray(foo);
delete [] foo;

Passing structure by reference and assign string

Here is a simple program where I am trying to pass a structure to a function by reference and a string. The function is supposed to detect the length of the string and assign it a member of the structure. Here is the program:
#include <iostream>
#include <string.h>
struct stringy // structure definition
{
char *str;
int ct;
};
void set(stringy &beany, const char *testing); // function definition
int main()
{
stringy beany;
char testing[] = "Reality isn't what it used to be.";
set(beany, testing); // function call
return 0;
}
void set(stringy &beany, const char *testing) // function prototype
{
int i=0;
while (*(testing+i) != '\0') // this loop counts the number of characters
{
i++;
std::cout << i << "\n";
}
beany.str = new char[i]; // dynamic storage allocation
std::cout << strlen(beany.str); // printing the length of the string
}
For some reason the output of the last line in the function set() is 47 while the value of "i" is 33. The last 15 bytes are filled with garbage value. I want that the length of beany.str should be equal to the length of *testing.
You allocate memory for beany.str but you don't initialize that memory. The contents of the allocated memory, without any initialization, is indeterminate (and in practice will be seemingly random).
Also don't forget that old C-style strings needs to be terminated by the special '\0' character (or functions like strlen will not work).
Both of these problems, using uninitialized memory and forgetting the terminator, will lead to undefined behavior.
beany.str = new char[i]; // dynamic storage allocation
std::cout << strlen(beany.str); // printing the length of the string
strlen looks for the terminating null character '\0'. There is no guaranteed one in beany.str, because you assign it the result of new char[i], which does not zero-initialize the elements. It allocates space for i characters that are not initialized to zero.
Even if they were, strlen would return 0, because it would immediately find '\0' at the first position. If you don't somehow remember i yourself, the size information will be lost.
Look at the output of the following program:
#include <iostream>
int main()
{
char *str = new char[100];
for (int i = 0; i < 100; ++i)
{
std::cout << str[i] << "\n";
}
}
The behaviour is undefined. What you will probably see are some seemingly random characters.
If you want zero-initialization, use new char[i]().
But still, strlen will be 0:
#include <iostream>
#include <string.h>
int main()
{
char *str = new char[100]();
for (int i = 0; i < 100; ++i)
{
std::cout << str[i] << "\n";
}
std::cout << strlen(str) << "\n";
}
You should just get rid of array-new and array-delete. Use std::string.

How to modify array that pointer points to

I need to pass a char pointer to function, then change the value that it points to inside the function and print values outside the function.
The problem I have is that I'm losing it when I leave function and try to print it outside. What can I do to avoid this?
This is an code example:
char array[] = "Bada boom";
char *pText = array;
reverseText(pText);
cout << (pText);
cout should print
moob adaB
When I print inside the function, everything is fine(it prints reversed).
My task is to print It out outside the function (as you can see in a 4th line of code)
This is the full of code which have the bug (printing inside func works, outside didn't work)
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
char reverseText(char *text);
int main(){
char array[] = "Bada boom";
char *pTekst = array;
reverseText(pTekst);
cout << (pTekst); //in here it doesn't work
}
char reverseText(char *text){
char befRev[100]; int lenght=-1;
/*until *text doesn't meet '\0' */
for(int i=0;*text!='\0';i++){
befRev[i]=(*text);
text++;
lenght++;
}
/*reversing*/
int j=0;
for(int i=lenght;i>=0;i--){
*(text+j)=befRev[i];
j++;
}
for(int i=0;i<=lenght;i++) //in here it does print the right value
cout << text[i];
};
Just re-arrange the array in-place. The pointer itself doesn't need to change:
#include <cstring>
#include <algorithm>
void reverseText(char* array)
{
auto len = std::strlen(array);
std::reverse(array, array+len);
}
int main()
{
char array[] = "Bada boom";
char *pText = array;
reverseText(pText);
std::cout << pText << std::endl;
}
Output:
moob adaB
If you really wanted to provide a pointer that points to a different address to the caller, you could simply return it:
char* foo(char* stuff)
{
char* tmp = ....;
...
// do some stuff
...
return tmp;
}
Alternatively, you could pass the pointer by reference, but the intent is less clear than in the previous version:
void foo(char*& stuff)
{
stuff = something_else;
}
But in both cases, you must make absolutely sure the thing the new pointer points to is valid outside of the function. This might require some dynamic memory allocation. For your case, it seems the best and simplest option is to re-arrange the array in place.
To answer your question, you have an error in logic. Notice that in your first loop in reverseText you increment the local pointer text. In your second loop you did not reset text to it's original value so beforeRev is being copied over starting at location text+offset.
If you were to look at pText on return from call to reverseText you would find it contains:
"Bada boom\0moob adaB"
Your reverseText should be renamed palindrome :)
This is pretty straightforward. Some points to note:
An array decays to a pointer when you pass it to a function.
You are passing in a null terminated string. So the length of the char array you are passing in is the length of the string (including white space) +1.
Because you are using a pointer there is no need to assign a temp variable to hold everything.
Here is some code in C that is easy to translate to C++. Working out the actual reverse algorithm is left for you as an exercise.
#include<stdio.h>
void reverseText(char* text)
{
// Hint: It can be done in one loop!
int i;
for(i = 0; i < 9; i++)
{
// Your algorithm to reverse the text. I'm not doing it for you! ;)
*(text + i) = 'r';
}
}
int main()
{
char array[] = "Bada boom";
reverseText(array);
printf("The text reversed: %s\n", array);
return 0;
}
My final code:
#include <iostream>
void reverseText(char* text){
int length=-1; char tmp;
/*Length = sign from 0 to 8 without counting explicit NUL terminator*/
for(int i=0;*(text+i)!='\0';i++){
length++;
}
int j=0; int i=length;
while(j<i){
tmp=*(text+j); //tmp=first
*(text+j)=*(text+i); //first=last
*(text+i)=tmp; //last=tmp
j++;
i--;
}
}
int main(){
char array[] = "Bada boom";
char *pText = array;
reverseText(pText);
std::cout << pText;
}
I should have read more about pointers before I started this exercise.
You can either return a pointer or pass a pointer to pointer as a function argument.
//pointer to pointer
void reverseText(char** textPtr) {
char* newText = ...; //initialize;
...
*textPtr = newText; //assign newText
}
//return pointer
char* reverseText(char* text) {
char* newText = ...; //initialize
return newText;
}
Remember that if you allocate memory in this function you must do it dynamically (with new or malloc) and you have to free it afterwards (with delete or free respectively). Memory allocation in a function like this is probably a bad practice and should be avoided.

Dynamic Object in c++ doesn't work

#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;
class STRING {
private:
char *S[10];
public:
STRING();
void set_str(int n, const char* str1);
char* get_str(int n);
};
STRING :: STRING(){
for (int i=0; (i < 9); i ++ ){
S[i] = '\0';
cout << S[i];
}
}
void STRING :: set_str(int n,const char*str1) {
S[n] = (char*)malloc(strlen(str1 + 1));
strcpy(S[n], str1);
cout << S[n];
}
char* STRING :: get_str(int n){
return S[n];
}
int main () {
cout << " Init \n";
STRING* str = new STRING();
cout << "Error \n";
return 0;
This program compiles successfully, runs, but when it runs it only works before the inicialization of object STRING* str = new STRING();. So I can't see in this case Error message. Could someone point me my mistake, please?
And if there is a better way to initialize the array of strings, I would be happy to know.
My goal is to initialize tha array of stings. And initially set values of the whole array to NULL.
Regards
Assuming STRING means "a collection of 10 strings", then it's your constructor. You are setting the pointers to the value of the character '\0' and not to a null pointer. You're assigning a char type to a char* type. Now that may work, but then you have the cout statement. And it tries to DEREFERENCE the pointer. But the pointer is pointing to wherever in memory '\0' is, not a null character at that point.
Edit: In response to the OP's question, here's how I think it should be done for initializing to 10 empty char arrays. Your constructor will change to:
STRING :: STRING(){
for (int i=0; i < 10; i ++ ){ // changed to 10. 9 would miss the 10th element
S[i] = new char[1]; // Length-1 char array
S[i][0] = 0; // or '/0' or whatever
cout << S[i]; // Prints nothing
}
}
Also you need a destructor to free the memory:
STRING::STRING~()
{
for(int i = 0; i < 10; i++) {
delete [] S[i];
}
And change your "set" method to use "new" and not "malloc". Or everything to malloc and free. But don't mix them. And remember to use "array delete" and not just delete.
And FTLOG, go to and bookmark this link. Use the built-in string class.
The correct declaration of the member should be:
char S[10];
The way you have it
char *S[10];
declares an array of 10 char *. So when you do S[i] = '\0';, you're initializing S[i], which is a pointer to a char, to '\0', which is probably NULL, so you're basically calling cout << NULL on the next line.