Trouble with a Pointer to an Array of Structures in C++ - c++

My assignment is to translate a phrase from pig Latin to English using a structure. My project thus far takes in a string, removes all capitalization, all punctuation besides the ending punctuation, and splits the string into an array of structures comprised of words. I am then supposed to, through the return statement specifically, return a pointer to my array of structures. When back in main, I want to create another array of structures identical to that in my pigLat function that I will be able to send to a new function for the second part of my project (which will consist of translating the pig latin to english).
The problem: trying to create a new array using the pointer causes a core segmentation fault.
Any help explaining why this doesn't work, and explaining what might work better would be appreciated!
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
using namespace std;
struct Word //Variable type to hold each word
{
string piglatin;
string english;
};
Word *pigLat(int &);
int main()
{
int size;
Word *phrase;
phrase = pigLat(size); //Passes value of size by reference, and returns ptr to structure
Word pigSent[size];
//THIS IS WHERE I GET SEGMENTATION FAULT************************************
for (int count = 0; count < size; count++)
{
pigSent[count].piglatin = phrase[count].piglatin;
}
//*************************************************************************
return 0;
}
//Receives a phrase in pig latin, finds # of words in phrase, seperates pig latin from english, returns pig latin
Word *pigLat(int &sizeOf)
{
string phrase; //Variable to hold pig latin phrase
cout << "Enter a phrase in pig latin: "; //User enters pig latin phrase
getline(cin, phrase);
char punctuation = phrase[phrase.length() - 1]; //Assumes last char is punctuation, and saves it
//Removes all characters besides last period
char removch[] = "&,'?.!-";
for (int count = 0; count < 7; count++)
{
phrase.erase(remove(phrase.begin(), phrase.end(), removch[count]), phrase.end());
}
int length = phrase.length(); //Number of elements in updated string
phrase.insert(length, 1, punctuation); //Inserts final punctuation at end of phrase
//Removes all capitalization
for (int count = 0; count < length; count++)
{
if(phrase[count] >= 'A' && phrase[count] <= 'Z')
{
phrase[count] = tolower(phrase[count]);
}
}
int index = 0;
int count = 0;
int *spaceElements = 0;
spaceElements = new int[length]; //Dynamically allocates spaceElements memory
for (count; count < length; count++) //Gives number of white spaces in phrase
{
if (phrase.find(' ', count) != -1)
{
int space = phrase.find(' ', count);
count = space;
spaceElements[index] = space;
index++;
}
}
sizeOf = (index + 1);
Word sentence[sizeOf];
int start = 0;
int end = 0;
count = 0;
//Copies, word by word, into Word array sentence
for (count; count < sizeOf; count++)
{
for (count; count < index; count++)
{
end = spaceElements[count] - start;
sentence[count].piglatin = phrase.substr(start, end);
start = spaceElements[count] + 1;
}
sentence[count].piglatin = phrase.substr(start, length);
}
//Testing***************************************************
for (count = 0; count < sizeOf; count++)
cout << endl << sentence[count].piglatin << endl;
//**********************************************************
delete [] spaceElements;
Word *ptrToSet = sentence; //assigns pointer to memory address of sentence array
return ptrToSet;
}

The pigLat() function instantiates sentence in the local function scope:
Word sentence[sizeOf];
PigLat() returns a pointer to the first element of this array:
Word *ptrToSet = sentence;
return ptrToSet;
However, once pigLat() returns, because sentence was a locally-scoped object, it goes out of scope and gets destroyed. Subsequent attempts to dereference the returned pointer is undefined behavior. This is the likely reason for your application crash. There may be other reasons too, I didn't look any further.

Related

c++ segmentation fault and Trace/breakpoint trap when using Dynamic memory allocation

I am practicing Dynamic memory allocation in C++. I wanna enter some word which length is less than 10, and when I enter "-1", it will print the word I have entered before.
But when I enter more than three words, it will give me an Trace/breakpoint trap or segmentation fault. I use gdb-peda to check my code, it said "double free detected in tcache 2", I think the problem is the "delete" part in my code, but I dont know how to correct it.
the gdb-peda reply
this is my code:
#include <iostream>
#include <limits>
using namespace std;
int counter = 0; //be used to count how many times we have entered
char **ins();
int main() {
auto dict = ins(); //Total list
for ( int i = 0; i < counter; i++ ) {
for ( int j = 0; j < 10; j++ ) {
if ( *( *( dict + i ) + j ) == '\0' ) //if the word is null,print the word and go to next word
break;
cout << *( *( dict + i ) + j );
}
cout << endl;
}
for ( int i = 0; i < counter; i++ )
delete[] dict[i]; //delete the total list
system( "pause" );
return 0;
}
char **ins() {
auto dict = new char *[1](); //total list
auto cpdict = new char *[1](); //copy list
while ( 1 ) {
auto word = new char[10](); //a list be used to store the entered word (length<10)
cout << "input word: ";
cin.get( word, 10, '\n' ); //get the entered word in the list
if ( word[0] == '-' && word[1] == '1' ) { //if enter "-1" , delete the copy list and return the total list
delete[] cpdict;
return dict;
}
counter++; //plus one of the times of entering
delete[] dict; //delete the old total list
auto dict = new char *[counter](); //create a new total list, the lenth is one more than the old total list
for ( int i = 0; i < counter - 1; i++ ) *( dict + i ) = *( cpdict + i ); //copy the stuffs in old copy list to new total list
*( dict + counter - 1 ) = word; //copy the word we just enter into total list
delete[] cpdict; //delter the old copy list
auto cpdict = new char *[counter](); //create a new copy list
for ( int i = 0; i < counter; i++ ) *( cpdict + i ) = *( dict + i ); //copy the stuffs in new total list to new copy list
cin.clear();
cin.ignore( numeric_limits<std::streamsize>::max(), '\n' );
}
}
My imagine input and output is like:
input word: aaa
input word: bbb
input word: ccc
input word: ddd
input word: -1
output:
aaa
bbb
ccc
ddd
First of all, try not to use using namespace std;.
I have also found a memory leak in your code. You allocate the word pointer every iteration but did you make sure to delete it when you get to the next iteration? What you need to do is, delete the word buffer at the end of the iteration. Plus you also have two variables dict and cpdict which you allocate memory for no reason, delete them later on in the while loop only to assign a new block of memory. Try to avoid unwanted allocations as it'll only slow things down. Frankly its hard to debug your code (mainly because your working with a lot of pointers) so ill show you a good implementation (ill add comments to show important notes).
#include <iostream>
#include <limits>
//using namespace std; <-- Dont use this, ever.
/**
* Lets create a struct named Word so that its easier to work with and debug.
* Also we can use a normal stack allocated array because we know the maximum word size beforehand and it has the added
* benifit of not needing to deallocate it at the end of each iteration.
*/
struct Word {
char mWord[10];
};
int counter = 0; //be used to count how many times we have entered
Word* ins();
int main() {
auto dict = ins(); //Total list
for (int i = 0; i < counter; i++) {
// Here you dont have to worry about null terminations as itll automatically gets terminated because the
// default values of all the elements in the word array is 0.
std::cout << dict[i].mWord;
std::cout << std::endl;
}
// Here you just have to delete the array itself.
delete[] dict;
system("pause");
return 0;
}
Word* ins() {
Word* dict = nullptr; // The array of words. Dont allocate anything here because its unwanted.
while (1) {
Word word; // Instantiate the struct.
std::cout << "input word: ";
std::cin.get(word.mWord, 10, '\n'); //get the entered word in the list
if (word.mWord[0] == '-' && word.mWord[1] == '1') { //if enter "-1" , delete the copy list and return the total list
return dict;
}
counter++; //plus one of the times of entering
// The instruction pointer came here because there was a new word inserted which is not "-1" so we need to
// add it to the array. Lets first allocate a new buffer.
Word* cdict = new Word[counter];
// If we had an array before, lets copy the old content to the new buffer using memcpy() and delete the old block.
if (dict)
{
memcpy(cdict, dict, sizeof(Word) * (static_cast<size_t>(counter) - 1));
delete[] dict;
}
// Now we can add the new word to buffer.
cdict[counter - 1] = word;
// Now lets assign the buffer to the main array pointer. Remember, we dont delete the buffer as this buffer
// contains the actual data. We will have to delete it at the end when the program terminates.
dict = cdict;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
Try not to use raw pointers as it'll make it harder to understand what's going on and it'll be a lot painful to debug in these types of errors. The C++ standard library provides a lot of nice options like std::string, std::vector and use Smart Pointers whenever possible.

storing a string to a 2d char array

I am trying to store a sentence to a 2d array by separating each words. In the 2d array each row will contain each word from the sentence. Here is what I think I should do.
//Logic
//given string mystring
string mystring = "testing the arrays";
//create a 2d char array to hold 4 words with 10 max size
char 2darr[4][10] = {" "};
int x = 0;
for (int i = 0,j=0; i <mystring.length(); i++)
{
if (mystring(i) != ' ')
2darr[x][j++] = mystring(i); //copy the each character to the first row
else
2darr[x][j++] = '\0';
++x; // goes to next row
j = 0; //reset j for new row
}
Is there a better way to do this? I think my logic is a little off as well
The better way to do this is:
1) There is no need to check spaces. For this to occur, you can use std::istringstream with operator >> to obtain each word in a loop.
2) Use strncpy to copy the string into the 2 dimensional array
3) You need to make sure that the string does not exceed the bounds of the array, and that you have no more than 4 separate words.
Here is an example:
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstring>
int main()
{
char arr2d[4][10] = {};
std::string mystring = "testing the arrays";
// create an input stream containing the test string
std::istringstream strm(mystring);
std::string word;
// this is the word count
int curCount = 0;
// obtain each word and copy to the array
while (strm >> word && curCount < 4)
strncpy(arr2d[curCount++], word.c_str(), 9);
// output the results
for (int i = 0; i < curCount; ++i)
std::cout << arr2d[i] << "\n";
}
Output:
testing
the
arrays
this expression char 2darr[4][10] = {" "} will only set the first element to be " ", the others will be '\0' or NULL. But it is probably OK, since it is the default terminator of C-strings.
Variables can't start with a digit, call it arr2d instead.
String character access is mystring[i], not mystring(i)
You only indented the lines in the else block, in C++ if you don't enclose a block with curly braces, it only captures the first row, what you basically wrote is:
else {
2darr[x][j++] = '\0';
}
++x; // goes to next row
j = 0; //reset j for new row
Corrected code is
std::string mystring = "testing the arrays";
//create a 2d char array to hold 4 words with 10 max size
char arr2d[4][10] = { };
int x = 0;
for (int i = 0, j = 0; i < mystring.length(); i++)
{
if (mystring[i] != ' ') {
arr2d[x][j++] = mystring[i]; //copy the each character to the first row
}
else {
arr2d[x][j++] = '\0';
++x; // goes to next row
j = 0; //reset j for new row
}
}

Reading in words to a dynamically allocated array pointing to a struct

Im trying to take the string the user enters which will then clean it up a bit and store word by word in a dynamically allocated array. The array points to a struct. Im trying to store the array in the "English" struct variable.
Here is what i came up with but when i run the code my test cout of the array shows no output so i feel as the words are not being copied to the array.
//This program will take in a English sentence from the user where it will then store the string in an dynamically allocated array
//where it will be run through a functions which will convert the English sentence to pig Latin. The
//final sentence will be displayed to the screen for the user to see.
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
struct Word{
string english;
string piglatin;
};
Word * sentenceSetup(string, int &);
int main()
{
int Size=1;//default how many words in the sentence.
string userSent;
//ask the user for a sentence they want to convert
cout<<"Hello, please enter a string to convert to PigLatin: "<<endl;
getline(cin, userSent);
Word * arr = sentenceSetup(userSent,Size);
for (int i=0; i<Size;i++)
{
cout<< arr[i].english<<endl;
}
return 0;
}
//**************************************************************
//sentenceSetup Definition: In this function we will be asking *
//the user to enter in a string which then will be counted *
//for how many words it has and creating an array of structs *
//big enough to hold that string. The array will then be *
//returned to the calling function. NOTE: This function should *
//remove all capitalization and special characters except for *
//the end period, exclamation mark, or question mark. *
//**************************************************************
Word * sentenceSetup(string userSent, int &size)
{
char nextCharacter;
//check the input for any unwanted special characters not listed in the function def.
for( int i=0; i<int(userSent.size()); i++)
{
nextCharacter = userSent.at(i);
if(ispunct(userSent[i]))
{
userSent.erase(i--, 1);
}
}
//change the whole sentence to lower case
for (int i=0; i<int(userSent.size()); i++)
{
userSent[i]=tolower(userSent[i]);
}
//Check each character in the string to see if it is a space. If the loop
//notices a space then a space equals a word in the string.
for (int i =0; i<int(userSent.size());i++)
{
nextCharacter = userSent.at(i); //Reads the character
if(isspace(userSent[i]))
{
size++;
}
}
//test to see if num count works
cout<<"There is "<<size << " words in the sentence."<<endl;
//test to see if special characters removed
//cout<<userSent<<endl;
//allocate an array to store the words in for the struct Word
Word *temp= new Word[size];
int count =0;
string word;
for(count=0;count<size;count++)
{
if(isspace(userSent[count]))
{
word =userSent.substr(0,count);
temp[count].english=word;
userSent.erase(0,count);
}
}
//test
for(count =0; count<size;count++)
{
cout<<temp[count].english;
}
return temp;
}
The problem lies in how you are trying to write the data into dynamically allocated Word* temp variable as well as the for loop you are using for the same. The above code needs to be rectified as follows
I'm creating a main array of strings which will be returned by the function
//allocate an array to store the words in for the struct Word
Word *main= new Word[size];
int count =0;
string word;
//Variable to keep track of start of each word
int start_count =0;
//Create a temp pointer to iterate through the memory allocated for each word
Word *temp = main;
//Variable to keep track of length of each word
int bytes_to_cpy =0;
The for loop should loop until the length of the sentence, not just the number of words
for(count=0;count<=userSent.length();count++){
if(isspace(userSent[count]))
{
//Copy the required bytes as a substring
word =userSent.substr(start_count,bytes_to_cpy);
//Copy the word to the main array
temp->english=word;
//Increment the pointer;
temp++;
//Ignore the white space for the next word
start_count=count+1;
//Reset the length of the word
bytes_to_cpy=0;
}
//Count the length of the word
bytes_to_cpy++;
}
//Add the last word to the array
word =userSent.substr(start_count,userSent.length()-start_count);
temp->english = word;
temp = main;
//test
for(count =0; count<size;count++)
{
cout<<temp->english<<endl;
temp++;
}
return main;
Apart from this, you also need to take care of multiple whitespaces, whitespace at the beginning and at the end of the user input string in our Function that calculates the number of words. Your current implementation does not take care of these.

C++ Copying an array of chars using char* (no string libraries)

I am writing a C++ function that is supposed to duplicate an array of chars by copying each element character-by-character into a new array. Ideally, if I make the statements
char* a = "test";
char* b = copyString(a);
then both a and b should contain the string "test." However, when I print the copied array b, I get "test" plus a series of nonsense characters that seem to be the pointer. I don't want those, but I can't figure out where I'm going wrong.
My current function is as follows:
char* copyString(char* s)
{
//Find the length of the array.
int n = stringLength(s);
//The stringLength function simply calculates the length of
//the char* array parameter.
//For each character that is not '\0', copy it into a new array.
char* duplicate = new char[n];
for (int j = 0; j < n; j++)
{
duplicate[j] = s[j];
//Optional print statement for debugging.
cout << duplicate[j] << endl;
}
//Return the new array.
return duplicate;
}
For the purposes of understanding certain aspects of C++, I cannot use string libraries, which is where other answers I have found have fallen short in this case. Any help with this problem is greatly appreciated.
EDIT: I though my stringLength function was fine - perhaps I was wrong.
int stringLength(char* s)
{
int n;
//Loop through each character in the array until the '\0' symbol is found. Calculate the length of the array.
for (int i = 0; s[i] != '\0'; i++)
{
n = i + 1;
}
//Optional print statement for debugging.
// cout << "The length of string " << s << " is " << n << " characters." << endl;
return n;
}
You need to copy the 0 too. That's what a C-style string is, a null-terminated character array.
Really, all you need to do is add one to the length:
int n = stringLength(s) + 1; // include the '\0'
And then everything else will account for itself - you'll allocate an array of sufficient size, and copy the '\0' in your loop too.

I am getting a bunch of extra chars when i try to reverse a cstring in c++

The string entered by the user appears to be reversed but it is also followed by a bunch of garbage chars. Here is my reverse function:
void reverse(char str[])
{
char reversed[MAX_CHAR];
for(int i = 0; i < strlen(str); i++)
{
reversed[i] = str[strlen(str) - i - 1];
}
cout << reversed << endl;
}
You need to end your string with the NULL character '\0', ASCII value 0.
Try this:
void reverse(char str[]) {
char reversed[MAX_CHAR];
int len = strlen(str);
for(int i = 0; i < len; i++) {
reversed[i] = str[len - i - 1];
}
reversed[len] = '\0'; // Add this
cout << reversed << endl;
}
This is how C and C++ know where the end of the string is.
Read more about Null-terminated character sequences:
http://www.cplusplus.com/doc/tutorial/ntcs/
http://en.wikipedia.org/wiki/Null-terminated_string
you can solve this in 2 ways either by
a) initializing reversed array with null chars
char reversed[MAX_CHAR] = {0};
b) adding null character by end of the reversed string.
reversed[strlen(str)] = '\0';