Dynamic memory

M

meyousikmann

I am having a little trouble with dynamic memory allocation. I am trying to
read a text file and put the contents into a dynamic array. I know I can
use vectors to make this easier, but it has to be done using dynamic arrays.
I don't know the size of the text file ahead of time, though, so I created a
class that includes a method to resize the array. Here is that class:

class Data
{
public:
Data(int initialsize);
~Data();
char **array;
void resize(int newsize);
void add(const int index, const string temp);
private:
int size;
int high;
int low;
};

Data::Data(int initialsize)
{
size = initialsize;
array = new char*[size];
}

Data::~Data()
{
delete array;
}

void Data::resize(int newsize)
{
char **newarray;

newarray = new char*[newsize];
memcpy(newarray, array, size*sizeof (char*));
size = newsize;
delete array;
array = newarray;
}

Now, here is my driver program where I am reading the data and trying to put
into the array from the Data class above:

#include <iostream>
#include <fstream>
#include <string>
#include "Data.h"

using namespace std;

int main()
{
char filename[14];
ifstream infile;
string temp;

cout << "Please input filename: ";
cin >> filename;
cout << endl;

infile.open(filename);

if(infile.fail())
cout << "Invalid filename." << endl;
else
{
int size = 0; // initial size of data array (zero because we don't yet
know how many lines there are)

Data myData(size); // create a data array object

// keep reading lines from the data file until end of file is reached
while(!infile.eof())
{
myData.resize(++size); // add space to the array object for another data
line
getline(infile, temp, '\n');
strcpy(myData.array[size - 1], temp.c_str());
}

}

return 0;
}

The data file is simply a multiline text file with each line terminated with
a CRLF.

Everything compiles, but when I go to run it, I get a message that says
"Unhandled exception blah blah blah" when the strcpy(myData.array[0],
temp.c_str()); line is executed.

Can anyone help me figure out how to make this work?

Thanks
 
K

Kai-Uwe Bux

meyousikmann said:
I am having a little trouble with dynamic memory allocation. I am trying
to
read a text file and put the contents into a dynamic array. I know I can
use vectors to make this easier, but it has to be done using dynamic
arrays.
Why?

I don't know the size of the text file ahead of time, though, so I
created a
class that includes a method to resize the array. Here is that class:

class Data
{
public:
Data(int initialsize);
~Data();
char **array;

This is an array of C-style strings. You should consider

std::string array [];

instead. That would allow you to focus on managing a dynamic array. The
approach you have taken forces you to simultaneously deal with the memory
management of the C-strings. That is where your problems come from.
void resize(int newsize);
void add(const int index, const string temp);
private:
int size;
int high;
int low;
};

Data::Data(int initialsize)
{
size = initialsize;
array = new char*[size];

This allocates the array. But it leaves the task of allocating memory for
each C-string to the client.
}

Data::~Data()
{
delete array;

This only deallocates the array. Memory for the individual C-strings will
leak.
}

void Data::resize(int newsize)
{
char **newarray;

newarray = new char*[newsize];
memcpy(newarray, array, size*sizeof (char*));
size = newsize;
delete array;
array = newarray;
}

Now, here is my driver program where I am reading the data and trying to
put into the array from the Data class above:

#include <iostream>
#include <fstream>
#include <string>
#include "Data.h"

using namespace std;

int main()
{
char filename[14];
ifstream infile;
string temp;

cout << "Please input filename: ";
cin >> filename;
cout << endl;

infile.open(filename);

if(infile.fail())
cout << "Invalid filename." << endl;
else
{
int size = 0; // initial size of data array (zero because we don't yet
know how many lines there are)

Data myData(size); // create a data array object

// keep reading lines from the data file until end of file is reached
while(!infile.eof())
{
myData.resize(++size); // add space to the array object for another
data
line
getline(infile, temp, '\n');
strcpy(myData.array[size - 1], temp.c_str());

Hm, myData.array[size-1] is a char*. But you never actually allocated memory
for that one, did you? try:

char* dummy = new char [ temp.size() + 1 ];
myData.array[size-1] = dummy;
strcpy(dummy, temp.c_str());

However, using std::string[] instead of char** would be a way better option.
}

}

return 0;
}

The data file is simply a multiline text file with each line terminated
with a CRLF.

Everything compiles, but when I go to run it, I get a message that says
"Unhandled exception blah blah blah" when the strcpy(myData.array[0],
temp.c_str()); line is executed.

Can anyone help me figure out how to make this work?

Thanks
 
J

Jacques Labuschagne

meyousikmann said:
I am having a little trouble with dynamic memory allocation. I am trying to
read a text file and put the contents into a dynamic array. I know I can
use vectors to make this easier, but it has to be done using dynamic arrays.
I don't know the size of the text file ahead of time, though, so I created a
class that includes a method to resize the array. Here is that class:

There are a number of problems... Here are the highlights:
- Don't resize as often. Resizing is usually a nontrivial performance
hit.
- If you're allowed to, prefer an array of std::string over an array
of char*.
- When reading from a file, don't check eof() until after you've
tried to read. This is the most frequent mistake newbies make. See the FAQ.
- getline() extracts the delimiter ('\n') from the stream and throws
it away. I assume you actually want to append it to the string you're
storing...
- When deleting an array you *must* use delete[] rather than plain
old delete.
- There's no point starting the size of your array at 0. It's OK to
over-estimate.


Here's some food for thought:

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <cassert>

using namespace std;

class Data{
char** array; ///< Ideally this should be a string*, or
///< even vector<string>.
static const unsigned int initial_size = 16;
unsigned int size;
unsigned int next_line;

public:
Data(): array(new char*[initial_size]),
size(initial_size),
next_line(0)
{
char* dummy = NULL;
fill(array, array + size, dummy);
}

~Data(){
// Delete each line first, and then the array that held them.
for (unsigned i = 0; i < next_line; ++i){
delete[] array;
}
delete[] array;
}

char** begin(){ return array; }
char** end(){ return array + next_line; }

// There's no need to provide an index if we're always appending...
void append(const string& s){
if (next_line == size){
// Double the size each time rather than just adding 1.
resize(size<<1);
}
assert(array[next_line] == NULL);
array[next_line] = new char[s.size() + 1];
strcpy(array[next_line], s.c_str());
++next_line;
}

void resize(unsigned int new_size){
if (new_size <= size){
return;
}
char** new_array = new char*[new_size];
char* dummy = NULL;
fill(new_array, new_array + new_size, dummy);
// Copy the pointers to existing strings rather than
// copying their data.
copy(array, array + next_line, new_array);
delete[] array;
array = new_array;
size = new_size;
}
};

int main(int argc, char** argv){
if (argc != 2){
return EXIT_FAILURE;
}
ifstream in(argv[1]);
if (in.fail()){
return EXIT_FAILURE;
}
Data data;
string line;
while (getline(in, line, '\n')){
line+="\n";
data.append(line);
}
copy(data.begin(), data.end(), ostream_iterator<char*>(cout));
}
 
M

meyousikmann

Kai-Uwe Bux said:

There is a BUNCH of legacy code that is expecting the array of C-style
strings. I am not very comfortable with older C code so I am just trying to
make as small a change as possible. I just want to make a change that will
read the data from a data file and put into the array without screwing
everything up totally.
I don't know the size of the text file ahead of time, though, so I
created a
class that includes a method to resize the array. Here is that class:

class Data
{
public:
Data(int initialsize);
~Data();
char **array;

This is an array of C-style strings. You should consider

std::string array [];

Point well taken. I am going to try to do work in your suggestion.
instead. That would allow you to focus on managing a dynamic array. The
approach you have taken forces you to simultaneously deal with the memory
management of the C-strings. That is where your problems come from.
void resize(int newsize);
void add(const int index, const string temp);
private:
int size;
int high;
int low;
};

Data::Data(int initialsize)
{
size = initialsize;
array = new char*[size];

This allocates the array. But it leaves the task of allocating memory for
each C-string to the client.
}

Data::~Data()
{
delete array;

This only deallocates the array. Memory for the individual C-strings will
leak.

Yep, I see that. I will work on this as well.
}

void Data::resize(int newsize)
{
char **newarray;

newarray = new char*[newsize];
memcpy(newarray, array, size*sizeof (char*));
size = newsize;
delete array;
array = newarray;
}

Now, here is my driver program where I am reading the data and trying to
put into the array from the Data class above:

#include <iostream>
#include <fstream>
#include <string>
#include "Data.h"

using namespace std;

int main()
{
char filename[14];
ifstream infile;
string temp;

cout << "Please input filename: ";
cin >> filename;
cout << endl;

infile.open(filename);

if(infile.fail())
cout << "Invalid filename." << endl;
else
{
int size = 0; // initial size of data array (zero because we don't yet
know how many lines there are)

Data myData(size); // create a data array object

// keep reading lines from the data file until end of file is reached
while(!infile.eof())
{
myData.resize(++size); // add space to the array object for another
data
line
getline(infile, temp, '\n');
strcpy(myData.array[size - 1], temp.c_str());

Hm, myData.array[size-1] is a char*. But you never actually allocated
memory
for that one, did you? try:

char* dummy = new char [ temp.size() + 1 ];
myData.array[size-1] = dummy;
strcpy(dummy, temp.c_str());

You got it. Funny how things seem to work when the memory is allocated
properly. Thanks for that. I really appreciate the help.
However, using std::string[] instead of char** would be a way better
option.
}

}

return 0;
}

The data file is simply a multiline text file with each line terminated
with a CRLF.

Everything compiles, but when I go to run it, I get a message that says
"Unhandled exception blah blah blah" when the strcpy(myData.array[0],
temp.c_str()); line is executed.

Can anyone help me figure out how to make this work?

Thanks
 
M

meyousikmann

Jacques Labuschagne said:
meyousikmann said:
I am having a little trouble with dynamic memory allocation. I am trying
to read a text file and put the contents into a dynamic array. I know I
can use vectors to make this easier, but it has to be done using dynamic
arrays. I don't know the size of the text file ahead of time, though, so
I created a class that includes a method to resize the array. Here is
that class:

There are a number of problems... Here are the highlights:
- Don't resize as often. Resizing is usually a nontrivial performance
hit.
- If you're allowed to, prefer an array of std::string over an array of
char*.
- When reading from a file, don't check eof() until after you've tried
to read. This is the most frequent mistake newbies make. See the FAQ.
- getline() extracts the delimiter ('\n') from the stream and throws it
away. I assume you actually want to append it to the string you're
storing...
- When deleting an array you *must* use delete[] rather than plain old
delete.
- There's no point starting the size of your array at 0. It's OK to
over-estimate.


Here's some food for thought:

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <cassert>

using namespace std;

class Data{
char** array; ///< Ideally this should be a string*, or
///< even vector<string>.
static const unsigned int initial_size = 16;
unsigned int size;
unsigned int next_line;

public:
Data(): array(new char*[initial_size]),
size(initial_size),
next_line(0)
{
char* dummy = NULL;
fill(array, array + size, dummy);
}

~Data(){
// Delete each line first, and then the array that held them.
for (unsigned i = 0; i < next_line; ++i){
delete[] array;
}
delete[] array;
}

char** begin(){ return array; }
char** end(){ return array + next_line; }

// There's no need to provide an index if we're always appending...
void append(const string& s){
if (next_line == size){
// Double the size each time rather than just adding 1.
resize(size<<1);
}
assert(array[next_line] == NULL);
array[next_line] = new char[s.size() + 1];
strcpy(array[next_line], s.c_str());
++next_line;
}

void resize(unsigned int new_size){
if (new_size <= size){
return;
}
char** new_array = new char*[new_size];
char* dummy = NULL;
fill(new_array, new_array + new_size, dummy);
// Copy the pointers to existing strings rather than
// copying their data.
copy(array, array + next_line, new_array);
delete[] array;
array = new_array;
size = new_size;
}
};

int main(int argc, char** argv){
if (argc != 2){
return EXIT_FAILURE;
}
ifstream in(argv[1]);
if (in.fail()){
return EXIT_FAILURE;
}
Data data;
string line;
while (getline(in, line, '\n')){
line+="\n";
data.append(line);
}
copy(data.begin(), data.end(), ostream_iterator<char*>(cout));
}


Wow. Thanks for the great info. As you very accurately pointed out, I am a
newbie at this, but I am learning with help from great people like you.

Thanks!
 
K

Kai-Uwe Bux

meyousikmann said:
I am having a little trouble with dynamic memory allocation. I am trying
to
read a text file and put the contents into a dynamic array. I know I can
use vectors to make this easier, but it has to be done using dynamic
arrays. I don't know the size of the text file ahead of time, though, so I
created a
class that includes a method to resize the array. Here is that class:
[homegrown vector snipped]
Now, here is my driver program where I am reading the data and trying to
put into the array from the Data class above:

#include <iostream>
#include <fstream>
#include <string>
#include "Data.h"

using namespace std;

int main()
{
char filename[14];
ifstream infile;
string temp;

cout << "Please input filename: ";
cin >> filename;
cout << endl;

infile.open(filename);

if(infile.fail())
cout << "Invalid filename." << endl;
else
{
int size = 0; // initial size of data array (zero because we don't yet
know how many lines there are)

Data myData(size); // create a data array object

// keep reading lines from the data file until end of file is reached
while(!infile.eof())
{
myData.resize(++size); // add space to the array object for another
data
line
getline(infile, temp, '\n');
strcpy(myData.array[size - 1], temp.c_str());
}

}

return 0;
}


Here is a version using the standard library:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
std::string filename;
std::ifstream infile;

std::cout << "Please input filename: ";
std::cin >> filename;
std::cout << '\n';

infile.open(filename.c_str());

if(infile.fail()) {
std::cout << "Invalid filename.\n";
} else {
std::vector< std::string > data;

// keep reading lines from the data file
// until end of file is reached
while(!infile.eof()) {
std::string temp;
std::getline( infile, temp );
data.push_back( temp );
}

// print the data:
std::copy( data.begin(), data.end(),
std::eek:stream_iterator<std::string>( std::cout, "\n" ) );
}

return 0;
}

You should seriously consider using std::string and std::vector. Code
becomes way more easy to maintain. Note in particular that no memory
management is required on your part.


Best

Kai-Uwe Bux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top