1

All my program crashes because of delete [] meanings;, delete [] meanings;, delete [] temp_meaning; , when I remove these 3 lines it works fine, so probably I am using the delete wrongly ... can anybody enlighten me here please?

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

class Expression {

    char *word_with_several_meanings; // like "bank", "class"
    char **meanings; // a pointer to a pointer stores all meanings
    int meanings_ctr; // meanings counter

    //-----------FUNCTIONS------------------------------------------------
public:
    void word(const char* = NULL );
    void add_meaning(char * = NULL);
    char* get_word();
    int get_total_number_of_meanings();
    char* get_meaning(int meanx = 0);
    Expression(int mctr = 0); // CTOR
    ~Expression(); // DTOR
};

Expression::Expression(int mctr ) {
    meanings_ctr = mctr;    // Setting the counter to 0
    meanings = new char * [meanings_ctr]; // Allocate Space for meanings
}

Expression::~Expression() {

    while(meanings_ctr-->0){
    delete meanings[meanings_ctr];
    }
    delete [] meanings; // Deleting the memory we allocated
    delete [] word_with_several_meanings; // Deleting the memory we allocated
}

void Expression::word(const char *p2c )
{

    word_with_several_meanings = new char[strlen(p2c)+1];
    // copy the string, DEEP copy
    strcpy(word_with_several_meanings, p2c);
}

void Expression::add_meaning( char  * p2c)
{

    //meanings[ meanings_ctr ] = new char [strlen(p2c) + 1];
    //strcpy(meanings[ meanings_ctr++ ] , p2c);
    // temp 
    if (meanings_ctr < 1){
    meanings[ meanings_ctr ] = new char [strlen(p2c) + 1];
    strcpy(meanings[ meanings_ctr++ ] , p2c);
    }
    else {
int temp_ctr;
    char **temp_meaning;
    temp_meaning = new char * [meanings_ctr-1];
    for(temp_ctr =0; temp_ctr<meanings_ctr;temp_ctr++){
        temp_meaning[temp_ctr] = new char [strlen(meanings[ temp_ctr ]) + 1];
            strcpy(temp_meaning[temp_ctr], meanings[ temp_ctr ]);
    }
    for (temp_ctr =0; temp_ctr<meanings_ctr;temp_ctr++){
            delete meanings[temp_ctr];

    }
    delete [] meanings;

    meanings = new char * [meanings_ctr];
    for(temp_ctr =0; temp_ctr<meanings_ctr;temp_ctr++){
        meanings[ temp_ctr ] = new char [strlen(temp_meaning[temp_ctr]) + 1];
            strcpy(meanings[ temp_ctr ], temp_meaning[temp_ctr]);
    }
    meanings[ meanings_ctr ] = new char [strlen(p2c) + 1];
    strcpy(meanings[ meanings_ctr ] , p2c);
            for (temp_ctr =0; temp_ctr<meanings_ctr;temp_ctr++){
            delete temp_meaning[temp_ctr];
    }
    delete [] temp_meaning;
            meanings_ctr++;
    }


}

char * Expression::get_meaning( int meanx )
{

    return *(meanings+meanx);

}

char * Expression::get_word()
{

    return word_with_several_meanings;

}

int Expression::get_total_number_of_meanings()
{
    return meanings_ctr;
}


int main(void) {
    int i;
    Expression expr;
    expr.word("bank");
    expr.add_meaning("a place to get money from");
    expr.add_meaning("b place to sit");
    expr.add_meaning("4 letter word");
    expr.add_meaning("Test meaning");
    cout << expr.get_word() << endl;

    for(int i = 0; i<expr.get_total_number_of_meanings(); i++)
            cout << " " << expr.get_meaning(i)  << endl;
    Expression expr2;
    expr2.word("class");
    expr2.add_meaning("a school class");
    expr2.add_meaning("a classification for a hotel");
    expr2.add_meaning("Starts with C");
    cout << expr2.get_word() << endl;
    for( i = 0; i<expr2.get_total_number_of_meanings(); i++)
            cout << " " << expr2.get_meaning(i) << endl;

    Expression expr3;
    expr3.word("A very long test");
    char str[] = "Meaning_    ";
    for(int kx =0; kx<31; kx++){
            str[8] = ('A'+kx);
            expr3.add_meaning(str);
    }

    cout << expr3.get_word() << endl;
    for( int i = 0; i<expr3.get_total_number_of_meanings(); i++)
            cout << " " << expr3.get_meaning(i) << endl;
    return 0;
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Anarkie
  • 657
  • 3
  • 19
  • 46
  • 1
    try deleting the loops like while(meanings_ctr-->0){ delete meanings[meanings_ctr]; } and just use delete[] on meanings etc... – Carl Winder May 01 '12 at 12:22
  • 2
    Is there some reason why you're not using `std::string` and STL containers? – suszterpatt May 01 '12 at 12:24
  • What was `new`, must be `delete`'d. What was `new[]`, must be `delete[]`'d. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/03/66660.aspx – DCoder May 01 '12 at 12:43
  • Nb. you use `delete temp_meaning[temp_ctr]` -- that should be `delete [] temp_meaning[temp_ctr]`. – CodeClown42 May 01 '12 at 12:43
  • Reason for all noob stuff is Im a beginner and this is my first C++ course yes I come from C world because I just passed C at school and we started C++, for example I hear from many people vector can be used etc. but I cant because the prof didnt introduce it yet and wanting us to invent somehow our own vector class to learn C++ better :) – Anarkie May 01 '12 at 12:49
  • Your prof's not a fool. You might as well learn from the ground up. Sounds like you still haven't found a solution yet, so: *how do you know the program is crashing because of `delete`?* You should really explain things in more detail than just "my program crashes". – CodeClown42 May 01 '12 at 14:27

2 Answers2

4

This program exhibits signs of memory corruption that comes from the following statements:

    meanings_ctr = mctr;    // Setting the counter to 0
    meanings = new char * [meanings_ctr]; // Allocate Space for meanings

and

    meanings = new char * [meanings_ctr];

Since add_meaning() contains the following code:

if (meanings_ctr < 1){
   meanings[ meanings_ctr ] = new char [strlen(p2c) + 1];

you actually write at meanings[0], while you allocated 0 bytes for it. Since indices in C start from 0 for an array with the highest index at max_index you need to allocate max_index+1 elements. For an array with max_index = 0 you need to allocate 1 element.

In other words you need to allocate meanings = new char * [meanings_ctr + 1] instead of new char * [meanings_ctr], and temp_meaning = new char * [meanings_ctr] instead of new char * [meanings_ctr - 1].

As for the use of delete and delete[] the general rule is that what was allocated with new should be deallocated with delete and what was allocated with new[] should be destroyed with delete[]. There is a thread on it: delete vs delete[] operators in C++. And some good background can be found in the answers in how does delete know it is an array.

Here is how to debug the program without using any expensive tools or tools that are difficult to learn.

If you add debug prints into your constructor and destructor like this:

Expression::Expression(int mctr ) {
    meanings_ctr = mctr;    // Setting the counter to 0
    meanings = new char * [meanings_ctr]; // Allocate Space for meanings
    cout << "[debug] allocated " << sizeof(char*)*meanings_ctr << " bytes @" << 
             hex << meanings << dec << endl;
}

And

Expression::~Expression() {
    while(meanings_ctr-- > 0){
//       if(meanings[meanings_ctr]) delete [] (meanings[meanings_ctr]);
    }
    cout << "[debug] to deallocate @" << hex << meanings << dec << endl;
//   delete [] meanings; // Deleting the memory we allocated
//    delete [] word_with_several_meanings; // Deleting the memory we allocated
}

and similarly in add_meaning(), you get

[debug] allocated 0 bytes @0x804c008
[debug] to deallocate @0x804c008
[debug] allocated 4 bytes @0x804c078
 ...
[debug] allocated 120 bytes @0x804fa78
[debug] to deallocate @0x804fa78
[debug] to deallocate @0x804c260
[debug] to deallocate @0x804c150

What looks worrying here is allocated 0 bytes. As the code in add_meanings() contains:

if (meanings_ctr < 1){
meanings[ meanings_ctr ] = new char [strlen(p2c) + 1];

it uses memory @ meanings[0] that was not allocated and leads to corruption.

For reference here are all the accumulated changes:

25c25
<     meanings = new char * [meanings_ctr]; // Allocate Space for meanings
---
>     meanings = new char * [meanings_ctr + 1]; // Allocate Space for meanings
30,31c30,31
<     while(meanings_ctr-->0){
<     delete meanings[meanings_ctr];
---
>     while(meanings_ctr-- > 0){
>         delete [] meanings[meanings_ctr];
58c58
<     temp_meaning = new char * [meanings_ctr-1];
---
>     temp_meaning = new char * [meanings_ctr ];
64c64
<             delete meanings[temp_ctr];
---
>          delete [] meanings[temp_ctr];
69c69
<     meanings = new char * [meanings_ctr];
---
>     meanings = new char * [meanings_ctr + 1];
76,77c76,77
<             for (temp_ctr =0; temp_ctr<meanings_ctr;temp_ctr++){
<             delete temp_meaning[temp_ctr];
---
>     for (temp_ctr =0; temp_ctr<meanings_ctr;temp_ctr++){
>            delete [] temp_meaning[temp_ctr];
Community
  • 1
  • 1
Dima Chubarov
  • 16,199
  • 6
  • 40
  • 76
  • I just changed `code` meanings = new char * [meanings_ctr + 1]; temp_meaning = new char * [meanings_ctr]; `code` But didnt help :( – Anarkie May 01 '12 at 13:20
  • 1
    There are a couple of other things that were mentioned in the comments that I did not put into the answer yet: use `delete [] (meanings[ctr])` instead of `delete meanings[ctr]`. The parentheses are important. Added a diff that contains all the changes. – Dima Chubarov May 01 '12 at 13:30
  • Can you elaborate - why are the parentheses `()` important in `delete [] (meanings[ctr])`? – DCoder May 01 '12 at 13:43
  • @DCoder not really. I do not hold C++ operator precedence table in memory and put parentheses when in doubt. – Dima Chubarov May 01 '12 at 13:45
  • delete [] (temp_meaning[temp_ctr]); and delete [] (meanings[meanings_ctr]); also didnt help I guess my code is mega corrupt :( and too complicated – Anarkie May 01 '12 at 13:55
  • It is not overly complicated at all. I have added a list of changes that I made to the code against the code you posted at the end of the answer. Please check the changes. This runs without error on linux with g++ and posts no errors when run with valgrind. – Dima Chubarov May 01 '12 at 14:00
  • Now it works fine with me as well! thanks a millions man :) I didnt change the second meanings = new char * [meanings_ctr]; I thought this was ok since meanings_ctr was incremented and would allocate enough memory when needed, but looks like not!So in my code I just needed to increase the memory allocation I arranged by +1 but why ? can we not write something to meanings[0] ? Should it always be meanings[1] at least? Also If you can tell why we deleted with delete [] temp_meaning[temp_ctr] instead of delete temp_meaning[temp_ctr], I would be grateful! – Anarkie May 01 '12 at 14:18
  • 1
    when you allocate 0 bytes as in `meanings = new char * [0]`, it is 0 bytes and to write to `meanings[0] = new char[x]` you need `sizeof(char*)` bytes. Since indices in C start from 0 the correct allocation adds 1 to the index of last element. As for delete[], if you allocate with `new` use `delete`, but if allocated with `new[]` a `delete[]` is required. – Dima Chubarov May 01 '12 at 14:30
  • Thank you very much now all clear, I approached new with the array way you know we always start with array[0] and there is always an element there, but now I understand! thanks again :) – Anarkie May 01 '12 at 14:40
3

You must come from the C world, you wouldn't find any char* in a C++ program to store a word... you should have a look at std::string, it will enlighten your day I can assure you.

By the way in C++ we tend to get rid of all those delete and delete [], you should maybe pick a good C++ book and learn proper C++, not a C with classes that has been used 30 years before.

Joe
  • 62,789
  • 6
  • 49
  • 67
Uflex
  • 1,396
  • 1
  • 13
  • 32
  • 1
    Boo. Does not address the question at all. And last I checked, `delete` was still a necessary and frequently used part of the language. – CodeClown42 May 01 '12 at 12:34
  • +1. It's not used that frequently anymore, since we got smart pointers. – MSalters May 01 '12 at 12:37
  • 1
    Fine, but there's no way that someone trying to learn the language should skip straight to smart pointers and never learn how to do it "the old fashioned way", lol. They'll be left out of the loop WRT *most* of the C++ code in the world, and situations where smart pointers aren't desirable. – CodeClown42 May 01 '12 at 12:45
  • 1
    @goldilocks: That's why I advised him to take a good C++ book that will be much more useful than reading any answer here. Even if knowing the "old way" of coding in C++ is obviously needed, I would recommend to take a recent book, presenting the C++ not from learning C then the object stuff. For me a book like _C++ primer_ (a 5th edition is on its way) or [Programming -- Principles and Practice Using C++](http://www2.research.att.com/~bs/programming.html) (haven't read that one but if written by Stroustrup, it must be good) are really worth buying and/or reading. – Uflex May 01 '12 at 14:21
  • Not that I mean to be *condescending* ;) ;) but I understand a little better now how you have become such an authority on the language that you can make these kinds of blanket statements (whilst dodging the real question). Sounds like the OP is in a uni course, if the prof wants them to learn memory management, it's probably good. At best: **this isn't an answer, it's a comment**. – CodeClown42 May 01 '12 at 14:36
  • Well Our Profs recommendation is also C++ primer plus 5th edition and I am on the Dynamic Memory allocation section now. – Anarkie May 01 '12 at 14:36
  • Well sorry, if I was (received) condescending or something like that, that was really not my point. I wanted first to answer the question but I ended just by leaving a comment in an answering post, my bad, I should have change from answer to comment, sorry again for that. – Uflex May 01 '12 at 14:46