-5

I am working in a Linux environment, c++. This simple code fails to release memory for the pointer, and I am unsure why.

If I implement code to allocate memory for vector pointer Var, and then properly clear out vector data using the swap() method to remove the vector entries, it also does not release the struct pointer. So I simplified the code to what you see below and it still does not release the memory when I call delete.

The use of a vector may be causing issues as there is no known size, not sure how to get around this. Either that or the standard library doesn't necessary free the memory back to the OS.

    #include <vector>

    typedef struct
    {
        char                Name[MAX_VAR_NAME_LEN + 1];    
        UINT8              *pValue;
    }DATA_STRUCT;

    typedef struct
    {
        char            SomeArray[SOME_MAX_ARRAY_SIZE;
        UINT16          VarCount;
        std::vector<DATA_STRUCT>  *Var;

    }SOME_STRUCT;

    int main(int argc, char* argv[])
    {
        SOME_STRUCT lpStruct* = new SOME_STRUCT;//lpStruct is allocated

        ///blah blah blah

        delete lpStruct;//lpStruct is not released

        return 1;
    }
  • You don't need those typedefs in C++, and it's rare that you need a pointer to a UINT8 or a vector, or indeed to perform explicit memory allocation at all. –  Oct 18 '18 at 21:58
  • 5
    Please provide a [mcve]. – George Oct 18 '18 at 21:59
  • With your exact example it should work fine, but I suspect you're also allocating memory for `Var`, which is _not_ being released. You don't need the pointer to the `std::vector` (likely), it just adds a layer of confusion. You should [edit] your question to show more info about `pValue` and `Var` – Tas Oct 18 '18 at 21:59
  • 1
    Your code reads like archaic Windows-API C, not like C++. You really don't need to be using pointers, `new`, `delete`, or `typedef` at all, and they're overcomplicating your code. I would gladly provide an answer and additionally show you what your code can be reduced to using modern C++, but your main question is very unclear. What do you mean that `lpStruct` "is not released?" In the code shown, you definitely release its memory and there is no problem. When doing `///blah blah blah`, do you allocate memory for `lpStruct->Var`? – alter_igel Oct 18 '18 at 22:24
  • Originally the code declared the vector not as a pointer, and Var had vector entries added to it. When I was ready to release the struct pointer memory, I cleared out the vector entries with a call to clear() and then did a swap() with a blank vector to ensure memory for vector was released. I am in Eclipse debugger looking at the struct pointer and when I called delete on it, I expected the pointer to go null. It did not. So I simplified the code, performing only the new on the struct, and then the delete. Still the pointer did not go null. Then I changed Var to be a pointer, same result. – JT Flach Oct 18 '18 at 22:49
  • So in its simplest from, if a struct has a vector in it, and you create a struct pointer, then allocate memory for it, then call delete on the struct pointer, the pointer does not go null. – JT Flach Oct 18 '18 at 22:49
  • Deleting memory does not cause the _pointer_ to point to`nullptr`, but does release the memory. You need to manually set it to `nullptr` afterwards. The pointer will continue to point at the memory it was pointing out, but the memory is no longer owned by your application – Tas Oct 18 '18 at 22:56
  • Ah. I will try that. – JT Flach Oct 18 '18 at 22:57

1 Answers1

0

Your main question seems to be why deleting a pointer doesn't set the pointed-to address to null. I'll answer this question, but also try to open your eyes to some more modern C++ styles towards the end of my answer.


In general, you're allowed to delete pointers that come from any expression, such as a function call. In that case, it's impossible to set the returned pointer to null:
Thing* getThingToDelete();
void main(){
    delete getThingToDelete();
    // how can delete set the returned pointer to null???
}

So if you own a pointer which is allowed to be null sometimes, it's good practice (but not required) to set that pointer to nullptr after you delete it, as a form of book-keeping. This is just something to get used to when dealing with pointers and manual memory allocation, as well as thinking rigorously about ownership, correctly using new, delete, new[], and delete[], checking for nullptr, and keeping track of array sizes. Really, it's a lot to think about, and it quickly gets in the way of what you set out to do in the first place. It's for this reason that a lot of modern C++ features exist to hide all this noise and take care of all the minutiae without the programmer's conscious intervention. An example follows below.


Consider this modern makeover of your code:
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

struct DATA_STRUCT {
    std::string Name;
    int Value;
};

struct SOME_STRUCT {
    // I'm not sure if SomeArray is supposed to be used as a string or array
    // so here's both:
    std::string SomeString;
    std::vector<char> SomeCharArray;

    // the need for VarCount is unclear, but if you want to get the number
    // of elements in Var, you simply write:
    // > Var.size()

    std::vector<DATA_STRUCT> Var;
};

int main(int argc, char** argv){
    SOME_STRUCT s;

    // some example usage:

    // is there anything in s?
    bool hasAnyVars = !s.Var.empty();

    // add a new value to s, with value=16 and name="Jeffrey"
    s.Var.push_back({ "Jeffrey", 16 });

    // how many items are in s now?
    size_t numItems = s.Var.size();

    // is there anyone named Helga?
    bool foundHelga = std::find_if(s.Var.begin(), s.Var.end(), [](const auto& x){
        return x.Name == "Helga";
    }) != s.Var.end();

    // print every thing in the list
    for (const auto& x : s.Var){
        std::cout << "Name=" << x.Name << ", value=" << x.Value << '\n';
    }

    // end of examples

    return 1;
}

Notice that I didn't use any pointers at all. Additionally, this code is free of memory leaks, and I never had to think about manual allocation. As you can see, modern C++ allows your program to have much richer, clearer semantics than before, and a large number of common tasks can be done if only a couple lines of code. There's a number of possibly unfamiliar things appearing in my examples that I'll provide links to here:

  • string - a super-simple, idiot-proof alternative to C-style strings
  • vector - obviously you've seen this, but the docs show many useful member functions you might not know about
  • iterators - generic pointer-like accessors into standard library containers, work very nicely with standard algorithms
  • find_if and friends from the <algorithms> library - convenient, generic search functions for any container
  • lambda expressions - anonymous, local functions which can also refer to local variables
  • the auto keyword - automatically deduces the type of a variable; can spare you lots of typing and maintenance work
  • ranged for-loops - the shortest, simplest way to write a for loop over all elements in a container

Also be aware that if you really need pointer semantics, such as having multiple references to an object or want to manage the lifetime of a polymorphic object simply, there is also:

  • unique_ptr - a smart pointer that automatically deletes itself when you are done using it. Only ever has one owner.
  • shared_ptr - a smart pointer that can have multiple references. Deletes itself when all places in your code are done using it.

I did leave your names largely unchanged, though it's uncommon nowadays to see code written in all capitals. It sometimes makes me worry that FORTRAN HAS RETURNED TO SHOUT AT ME but that's subjective of me, and it's really a matter of one's own preference.

alter_igel
  • 6,899
  • 3
  • 21
  • 40
  • Thank you for the detailed response! I would certainly agree there are cleaner safer ways to write this in C++, per your examples. My example was hastily written without regards for spelling, capitalization of Var names, or names that make sense, as I was only trying to demonstrate the issue I was concerned about. Yes, there are multiple references to the object in the real code, thus pointers to the struct and pointers to members in the struct. But the vector need not be a pointer and I will change that. I am certainly going to look at the vector members as I am not familiar with them all. – JT Flach Oct 19 '18 at 01:40