C++ Dynamic Ragged Array - c++

I have been trying to figure out this assignment for hours and can't grasp it yet. I'm trying to read in names from a txt document, which is working, and I need to store them in an char pointer array. Once the number of names is as big as the array size, I need to use REALLOCATION to make the array bigger (I can't use vector library).
I'm struggling with changing the name array size to make it bigger and deleting the old array from memory (it just crashes when I write delete [] names;).
Here is my currently broken code:
int numNames = 2;
char * names[numNames] = {};
ifstream namesFile("names.txt");
//get names from user, put names in ragged array
int i = 0;
while (i < numNames) {
if (namesFile.good() && !namesFile.eof()) {
//add name to ragged array
names[i] = new char[257];
namesFile >> setw(256) >> names[i];
i++;
if (i == numNames) {
//need a bigger array
numNames *= 2;
char * temp[20] = {};
for (int j = 0; j < numNames / 2; j++) {
temp[j] = names[j];
}
delete [] names;
names = temp;
}
}
else {
//end of file or corrupt file
break;
}
}
namesFile.close();
Any help is appreciated!

The following statement does not do any dynamic allocation. It just declares an array of pointers to char (char*) on a stack of size numNames. So, it is not dynamic by any means:
char * names[numNames] = {};
As such, you cannot change its size.
Instead you need to create a pointer to the array like the following:
char **names = new (char*)[numNames];
Same story for your temp down the road. Now you are free to delete/new and to assign pointers as well: names = temp.
Now, you need to read your char data from a file line by line and put it in the 'names' array. When you read a string, you can allocate space for it in the array member, i.e.
names[i] = new char [strlen(inputString) + 1]; // you need '+1' for termination char
and you can copy data from your string after allocation, i.e.
strcpy(name[i], inputString); // this is a standard 'c' string copy
you can also use std::copy or a for loop do do a copy or whatever. Another method is to use a standard 'c' malloc function to do allocation:
name[i] = (char *)malloc(strlen(inputString) + 1);
The difference is that when you would need to free memory, you would use delete[] name[i] in the first case and free(name[i]) in the second. Though it looks like you do not need to free the memory in your task.
Now you just have to figure out how to read from the file.

Related

c++ read into c-style string one char at a time?

in c++ id like to read input into a c-style string one character at a time. how do you do this without first creating a char array with a set size (you don't know how many chars the user will enter). And since you can't resize the array, how is this done? I was thinking something along these lines, but this does not work.
char words[1];
int count = 0;
char c;
while(cin.get(c))
{
words[count] = c;
char temp[count+1];
count++;
words = temp;
delete[] temp;
}
Since you cannot use std::vector, I am assuming you cannot use std::string either. If you can use std::string, you can the solution provide by the answer by #ilia.
Without that, your only option is to:
Use a pointer that points to dynamically allocated memory.
Keep track of the size of the allocated array. If the number of characters to be stored exceeds the current size, increase the array size, allocate new memory, copy the contents from the old memory to new memory, delete old memory, use the new memory.
Delete the allocated memory at the end of the function.
Here's what I suggest:
#include <iostream>
int main()
{
size_t currentSize = 10;
// Always make space for the terminating null character.
char* words = new char[currentSize+1];
size_t count = 0;
char c;
while(std::cin.get(c))
{
if ( count == currentSize )
{
// Allocate memory to hold more data.
size_t newSize = currentSize*2;
char* temp = new char[newSize+1];
// Copy the contents from the old location to the new location.
for ( size_t i = 0; i < currentSize; ++i )
{
temp[i] = words[i];
}
// Delete the old memory.
delete [] words;
// Use the new memory
words = temp;
currentSize = newSize;
}
words[count] = c;
count++;
}
// Terminate the string with a null character.
words[count] = '\0';
std::cout << words << std::endl;
// Deallocate the memory.
delete [] words;
}
You asked for C-style array. Stack or dynamic allocation will not serve you in this case. You need to change the count of the array number in each time you add new element which is not possible automatically. You have to work around and delete and reserve the array each time a new chae is read. So you have to options:
Use std::vector (which was created for this purpose)
Duplicate what is inside std::vector and write it yourself during your code( which seems terrible)
std::vector solution:
std::vector<char> words;
words.reserve(ESTIMATED_COUNT); // if you you do not the estimated count just delete this line
char c;
while(cin.get(c)){
words.push_back(c);
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1;
char c;
while (cin.get(c))
{
if (c == '\n')
continue;
s1 += c;
cout << "s1 is: " << s1.c_str() << endl; //s1.c_str() returns c style string
}
}
You have two ways, first is to use an zero size array, after each input you delete the array and allocate a new one that is +1 bigger, then store the input. This uses less memory but inefficient. (In C, you can use realloc to improve efficiency)
Second is to use a buffer, for example you store read input in a fixed size array and when it get full, you append the buffer at the end of main array (by deleting and re-allocating).
By the way, you can use std::vector which grows the size of itself automatically and efficiently.
If you're set on using c-style strings then:
char* words;
int count = 0;
char c;
while(cin.get(c))
{
// Create a new array and store the value at the end.
char* temp = new char[++count];
temp[count - 1] = c;
// Copy over the previous values. Notice the -1.
// You could also replace this FOR loop with a memcpy().
for(int i = 0; i < count - 1; i++)
temp[i] = words[i];
// Delete the data in the old memory location.
delete[] words;
// Point to the newly updated memory location.
words = temp;
}
I would do what Humam Helfawi suggested and use std::vector<char> since you are using C++, it will make your life easier. The implementation above is basically the less elegant version of vector. If you don't know the size before hand then you will have to resize memory.
You need to allocate a string buffer of arbitrary size. Then, if the maximum number of characters is reached upon appending, you need to enlarge the buffer with realloc.
In order to avoid calling realloc at each character, which is not optimal, a growth strategy is recommended, such as doubling the size at each allocation. There are even more fine-tuned growth strategies, which depend on the platform.
Then, at the end, you may use realloc to trim the buffer to the exact number of appended bytes, if necessary.

"Grow" function for dynamic arrays generating an error

Beginner programmer here.
The code below seems to always run into an error called: "pointer being freed was not allocated", and I can't figure out why.
A struct contains dynamic arrays to store integers and strings after reading in a file. This file contains a list of city names, high and low temperatures. Then, these are stored into the dynamic arrays after reading those lines, and grow the dynamic arrays if necessary (double the size).
Did I write something incorrect for this "grow" code function?
int i = 0;
if ( i >= arr1.size){ //arr1 is a int declared in a struct
string *tempStr; //temporary string
tempStr = new string[arr1.size*2];
int *tempInt; //temporary int
tempInt = new int[arr1.size*2];
for (int a = 0; a < arr1.size; a++){
tempStr[a] = arr1.cityName[a]; //cityName is a dynamic array declared in struct as a string
tempInt[a] = arr1.hiTemp[a]; //hiTemp --> dynamic array declared in struct as an int
tempInt[a] = arr1.loTemp[a]; //loTemp --> dynamic array declared in struct as an int
}
delete[] arr1.cityName;
delete[] arr1.hiTemp;
delete[] arr1.loTemp;
arr1.cityName = tempStr;
arr1.hiTemp = tempInt;
arr1.loTemp = tempInt;
arr1.size = arr1.size*2; //doubling the size
}
i++;
tempInt[a] = arr1.hiTemp[a];
tempInt[a] = arr1.loTemp[a];
You've using the same temp array for both.
And then you do:
arr1.hiTemp = tempInt;
arr1.loTemp = tempInt;
So now your struct has two different pointers pointing to the same array.
That means the next time something runs your grow algorithm on that struct, it'll get to:
delete[] arr1.hiTemp;
delete[] arr1.loTemp;
and consequently try to delete the same memory twice, which is of course very bad.
So you need to fix this to use two separate new int arrays.
(Or just replace all this with std::vectors since those are much easier to manage.)

Access Violation Writing to location

Im new to c++ and I dont know what this error means. It reads from a file and tries to store the values in a char * [].
The file contains:
5,Justin,19,123-45-6789,State Farm,9876,Jessica,Broken Hand,
This is my code.
void Hospital::readRecordsFile(){
std::ifstream fileStream;
fileStream.open(fileName); // Opens the file stream to read fileName
char * temp [8];
int i = 0;
while(!fileStream.eof()){
fileStream.get(temp[i],256,',');
i++;
}
i = 0;
for(char * t:temp){
std::cout << t << std::endl;
}
}
The error is at the line fileStream.get(temp[i],256,',');
You define an array of 8 pointers to char, but forget to allocate memory so that the pointers point to a valid chunk of memory:
char * temp [8]; // need then to allocate memory for the pointers
Because of this, in the line
fileStream.get(temp[i],256,',')
you end up using memory that's not yours.
Solution:
for(int i = 0; i<8; i++)
temp[i] = new char[256]; // we allocate 256 bytes for each pointer
Better though, use a std::vector<std::string> instead.
In the code you have right now it looks like you implicitly assume that the file has no more than 8 lines, which I find hard to believe. If your file has more than 8 lines, then you'll end up accessing the array of 8 pointers out of bounds, so you'll get another undefined behaviour (usually a segfault). That's why is much better to use standard STL containers like std::vector, to avoid all these headaches.
In case you MUST use pointers and want a variable number of lines, then you have to use a pointer to pointer,
char** temp;
then allocate memory for an enough pointers-to-char,
temp = new char* [1000]; // we believe we won't have more than 1000 lines
then, for each pointer-to-char, allocate memory
for(int i = 0; i < 1000; ++i)
temp[i] = new char[256];
At the end of the program, you must then delete[] in reverse order
for(int i = 0; i < 1000; ++i)
delete[] temp[i];
delete[] temp;
As you can see, it's getting messy.
You never allocated memory for each pointer in temp.
You probably want something like:
for (unsigned int i = 0u; i < 8; ++i)
{
temp[i] = new char[256];
}
The says that the temp variable points to 8 dynamically allocated byte buffers of 256 bytes each.

c++ Dynamic array using strings won't accept strings

I am trying to put the strings in a temporary array into a dynamic array. But the compiler just breaks when it hits that.
where dynaicArray is called:
string* dynamicArray = NULL;
Here is where it is breaking:
for (int i = 1; i <= (size); i++)
{
dynamicArray[i] = tempArray[i];
}
Where tempArray is filled:
void populateArray(int& size, string*& dynamicArray)
{
char decide;
string tempArray[100]; //Holds the strings until the size is known
bool moreStrings = true;
while (moreStrings == true)
{
cout << "\nEnter your string here:";
cin >> tempArray[size];
cout << "\nDo you want to enter another string? Y/N:";
cin >> decide;
decide = toupper(decide);
size ++;
dynamicArray = new string[size];
if (decide == 'N')
{
for (int i = 1; i <= (size); i++) //moves all of the strings from tempArray to dynamicArray
{
string temp;
temp = tempArray[i];
dynamicArray[i] = temp;
}
moreStrings = false;
}
}
}
PS: I know vectors are better. Unfortunately they're not an option.
Some design ideas:
the code in the if (decide == 'N') block is better placed after the while, to make the while smaller == more readable
once the above is implemented, you can set the moreStrings var directly with the result of your decide == 'N'; no need for an explicit if there anymore
you now do a dynamicArray = new string[size]; in each pass through the while, which is an enormous memory leak (you'r overwriting the newly created dynamic array with a new copy without reclaiming the old one out first - see dalete[])
as already mentioned: don't assume 100 will be enough - read "Buffer overflow" (only viable solution: make it a dynamic array as well and re-allocate it to a bigger one if it gets full)
better initialize size in the function before you use it - much safer; callers don't need to remember to do it themselves
C++ arrays are 0-based, so when you start copying them you'd also better start at 0 and not at 1
nitpick: for (int i = 1; i <= (size); i++): the () around size are superfluous
bonus advanced nitpick: use ++size and ++i in these contexts; it's a bit more efficient
you now use the var tmp to copy from the temp array to the dynamic one and the code is also somewhat structured to suggest you're using it to swap the strings between the two arrays (you're not) - rename the tmp variable or get rid of it altogether

C++ creating a two-dimensional array based on user input string length

I have an assignment to create a block transposition cipher program. A user is to input a phrase of their choice, and the program is to strip the phrase of spaces, punctuation, and make lowercase, before reading its length and creating a two-dimensional array the size of the nearest square that will fit all the chars in the mutated string, and filling in the remaining space with random letters.
Problem is, I'm having issues with creating that square.
I have this so far:
int main()
{
string input;
cout << "Please enter message to cipher." << endl;
getline(cin, input);
/* do punctuation removal/mutation */
int strLength = input.length(); //after mutation
/* need to find the square here before applying sizes and values to arrays */
char * original = new char[][]; // sizes pending
char * transposed = new char[][]; // sizes pending
for (int i = 0; i <= /* size pending */ ; i++)
{
for (int j = 0; j <= /* size pending */ ; j++)
{
transposed[j][i] = original[i][j];
}
}
/* do more stuff here */
}
any ideas?
(I already have done the mutation portion; tested with alternate code)
You can't do e.g.
char * original = new char[][];
First of all you are trying to create an array of arrays (or pointer of pointers) and assign it to a single pointer. You need to do it in two steps:
Allocate the "outer" array:
char **original = new char* [size];
Allocate the "inner" strings:
for (int i = 0; i < size; i++)
original[i] = new char [other_size];
However I would highly recommend against using that! Instead you should be using std::vector instead of "arrays" allocated on the heap, and if the contents are strings then use std::string:
std::vector< std::vector< std::string > > original;
You can take the square root of the length, round down to an integer, and add one to get the new length.
int len = (int)(sqrt(strLength) + 1e-9) + 1;
You'd then malloc the square using len and fill as you normally would.
I believe you do not need the "new" to create your storage. Following code should just do the job:
char buf[size][size]; // size is a variable
... // populate your buf
char tmp;
for(int i = 0; i < size; i++) {
for(int j = 0; j < i; j++) {
tmp = buf[i][j];
buf[i][j] = buf[j][i];
buf[j][i] = tmp;
}
}
This does the transpose in place. You don't need another array to store the char's.