Dynamic memory

Discussion in 'C++' started by meyousikmann, Oct 10, 2005.

  1. meyousikmann

    meyousikmann Guest

    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
     
    meyousikmann, Oct 10, 2005
    #1
    1. Advertising

  2. meyousikmann

    Kai-Uwe Bux Guest

    meyousikmann wrote:

    > 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
     
    Kai-Uwe Bux, Oct 10, 2005
    #2
    1. Advertising

  3. meyousikmann wrote:
    > 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));
    }
     
    Jacques Labuschagne, Oct 10, 2005
    #3
  4. meyousikmann

    meyousikmann Guest

    "Kai-Uwe Bux" <> wrote in message
    news:dichtm$nua$...
    > meyousikmann wrote:
    >
    >> 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?


    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

    >
     
    meyousikmann, Oct 10, 2005
    #4
  5. meyousikmann

    meyousikmann Guest

    "Jacques Labuschagne" <> wrote in message
    news:Ynk2f.16973$...
    > meyousikmann wrote:
    >> 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!
     
    meyousikmann, Oct 10, 2005
    #5
  6. meyousikmann

    Kai-Uwe Bux Guest

    meyousikmann wrote:

    > 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
     
    Kai-Uwe Bux, Oct 10, 2005
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Rick Glos
    Replies:
    0
    Views:
    648
    Rick Glos
    Jul 8, 2003
  2. Chris Thunell
    Replies:
    3
    Views:
    3,431
    Edward
    Jul 21, 2004
  3. s.subbarayan

    Dynamic memory allocation and memory leak...

    s.subbarayan, Mar 18, 2005, in forum: C Programming
    Replies:
    10
    Views:
    707
    Eric Sosman
    Mar 22, 2005
  4. Ken
    Replies:
    24
    Views:
    3,876
    Ben Bacarisse
    Nov 30, 2006
  5. Nephi Immortal
    Replies:
    1
    Views:
    239
Loading...

Share This Page