0

Written code to find and remove the largest word in a string without the using of library functions. Everything works fine. But when I want to free memory, the result is negative (displays an empty line). If you remove the call to the memory release function, everything will work correctly, but there will be a leak of memory. How do I fix it? Please help me.

#include <iostream>

using namespace std;

int length(char *text) // string length
{
    char *begin = text;
    while(*text++);

    return text - begin - 1;
}

int size(char **text) // size of two-dimensional array
{
    int i = 0;
    while(text[i]) i++;

    return i;
}

void free_memory(char **text)
{
    for(int i=0; i<size(text); i++)
        delete text[i];

    delete [] text;
}


char **split(char *text, char delim)
{
    int words = 1;
    int len = length(text);

    for(int i=0; i<len; i++)
        if(text[i] == delim) words++;

    char **result = new char*[words + 1];
    int j = 0, t = 0;

    for(int i=0; i<words; i++)
    {
        result[i] = new char[len];

        while(text[j] != delim && text[j] != '\0') result[i][t++] = text[j++];

        j++;
        t = 0;
    }

    result[words + 1] = nullptr;

    return result;
}

char *strcat(char *source, char *destination)
{
    char *begin = destination;

    while(*destination) destination++;
    *destination++ = ' ';
    while(*source) *destination++ = *source++;

    return begin;
}

char *removeWord(char *in_string)
{
    char **words = split(in_string, ' ');
    int max = length(words[0]);
    int j = 0;

    for(int i=0; i<size(words); i++)
        if(max < length(words[i]))
        {
            max = length(words[i]);
            j = i;
        }

    int index;
    char *result;

    if(!j) index = 1;
    else index = 0;

    result = words[index];

    for(int i=0; i<size(words); i++)
        if(i != j && i != index)
            result = strcat(words[i], result);

    free_memory(words); // I want free memory here

    return result;
}

int main()
{
    char text[] = "audi and volkswagen are the best car";

    cout << removeWord(text) << endl;

    return 0;
}
Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
Simon
  • 29
  • 3
  • 6
    Prefer `std::string` to character arrays. – Ron Nov 03 '17 at 13:25
  • 3
    why not using "modern" containers since you're using C++: strings & vectors. No memory leaks with those. – Jean-François Fabre Nov 03 '17 at 13:25
  • `result[i] = new char[len];`: missing 1 byte for nul-char – Jean-François Fabre Nov 03 '17 at 13:26
  • 5
    The easy answer is "use `std::string`". The correct but impractical answer is "`delete` what you `new`ed and `delete []` what you `new []`ed", but it's way too annoying to keep track of, so just use `std::string` instead. – nwp Nov 03 '17 at 13:26
  • You could use smart pointers to manage your memory deallocations. – Thomas Matthews Nov 03 '17 at 13:43
  • 2
    I'm downvoting this question, because even if it is perfectly reasonable to wonder about this, the whole design is completely messed up. Any answer that would answer your question would still be very bad C++ code. If you want to use this style of programming, use C. If you want to use C++, use provided containers, or at least use modern design. – klutt Nov 03 '17 at 13:44
  • Don't reuse a pointer from within the split words for the result, allocate new memory for the result and `strcat` onto that instead. And make sure to terminate your strings. – molbdnilo Nov 03 '17 at 13:56
  • 2
    If you use `std::string` and `std::vector`, most of your code will go away. The `length` function would be `s.length()`, the `size`would be `v.size()`, `free_memory`would not be needed, `strcat` would just be `+`, etc. You are doing *way* too much work yourself. – Bo Persson Nov 03 '17 at 14:03
  • @Jean-FrançoisFabre TIL `std::string` and `std::vector` are "modern" ;) – Quentin Nov 03 '17 at 14:03
  • it was modern in 1996 in the STL times. hence the quotes. – Jean-François Fabre Nov 03 '17 at 14:11
  • And BTW, you might also want to check the methods used in [How to split a string?](https://stackoverflow.com/questions/236129/the-most-elegant-way-to-iterate-the-words-of-a-string) – Bo Persson Nov 03 '17 at 14:13
  • 2
    The trick today in managing memory is to never write `new` or `delete` yourself. – AndyG Nov 03 '17 at 15:14
  • 2
    _"Written code to find and remove the largest word in a string without the using of library functions."_ Is some computing course asking you to do this? If so, why do they think it's useful? Or at least, why are they asking for this in a C++ course, when it's just C? Seeing the sheer amount of guff that seems to be requested in people's courses makes me glad that I taught myself how to program, and to program things that I had a concrete practical need for. – underscore_d Nov 03 '17 at 15:16
  • Note that `strcat()`, as coded here, is an infinite loop with `strcat(foo,foo);` – chux - Reinstate Monica Nov 03 '17 at 15:30

3 Answers3

2

In fact, this is C style programming - not C++. I see that your aim is to implement everything from scratch, possibly for practicing. But even then, your code is not designed/structured properly.

Besides that, you also have several bugs in your code:

  1. result[words + 1] = nullptr; must be result[words] = nullptr;

  2. You need result[i][t] = '\0'; after the while loop in split

  3. delete text[i] must be delete [] text[i]

  4. You cannot assign to your result pointer memory from words, then free it and then return it for use by the caller.

  5. There is at least one further bug in the second half of removeWord. It would be tedious to try to understand what you are trying to do there.

You might want to start with a simpler task. You also should proceed step-by-step and check each function for correctness independently first and not implement everything and then test. Also take a look at the tool valgrind for memory checking - if you use Linux.

Pedro
  • 842
  • 6
  • 16
1

The way you free memory correctly is to use RAII:

  • Only use new and new[] in constructors
  • Pair those with delete and delete[] in the corresponding destructor
  • Use automatic storage duration objects as much as possible

If you are specifically not using std::string and std::vector etc, for reasons of learning pointers, you will end up writing some small number of classes that resemble string and vector and unique_ptr, and then you go about programming as if you were using the std versions.

Caleth
  • 52,200
  • 2
  • 44
  • 75
  • The alternative is to replace `new type[...]` with `malloc(...)` and `delete ...` with `free(...)` and accept that you were writing C all along – Caleth Nov 03 '17 at 15:32
0

You have two issues. First is that result is assigned to a memory location in words. Second, is that you're storing the result of strcat in words[i] which will likely not have enough room (see strcat documentation).

result = new char[len(in_string)+1]; // +1 for space for null char

// the old loop reversed the word order -- if you want to keep doing
// that, make this a descending loop
for(int i=0; i<size(words); i++)
    if(i != j && i != index)
        strcat(result, words[i]);

free_memory(words);

return result;

So that when you free words, what result points to is also free'd. You would then need to free your result in main().

int main()
{
    char text[] = "audi and volkswagen are the best car";
    char * result = removeWord(text);
    cout << result << endl;

    delete[] result;

    return 0;
}
Phil Brubaker
  • 1,257
  • 3
  • 11
  • 14
dlasalle
  • 1,615
  • 3
  • 19
  • 25