-4

The C++ language has specific rules about how you must free the memory to which a pointer points. These rules require you to know how the memory was allocated in order to correctly free it.

For example,

int *p = new int();
std::free(p);

results in undefined behavior. (Reference)

In modern C++, we like to use smart pointers where possible, but sometimes we may want to use libraries or functions that allocate raw pointers. Suppose I want to use a function of the form:

char* NotMyFunction();

How do I properly manage the memory allocated by this function?

Apollys supports Monica
  • 2,938
  • 1
  • 23
  • 33
  • with a `unique_ptr` or a `shared_ptr` – Swordfish Sep 12 '18 at 20:35
  • 2
    The standard smart pointers can be given custom deleters. Use a deleter that cleans up appropriately, based on the requirements of `NotMyFunction()`. – François Andrieux Sep 12 '18 at 20:35
  • 7
    Read the function's documentation, it should tell you how the memory needs to be freed. You can still use a smart pointer wrapper, just give it an appropriate deleter to free the memory properly – Remy Lebeau Sep 12 '18 at 20:35
  • Is `NotMyFunction()` actually giving you a resource that you need to clean up? If it does then you can still use a smart pointer with a custom deleter. – NathanOliver Sep 12 '18 at 20:35
  • Shouldn't you use delete instead of std::free ? – Max Sep 12 '18 at 20:36
  • @RemyLebeau this should be an answer. – n. m. could be an AI Sep 12 '18 at 20:37
  • @Apollys Down votes don't mean the question isn't clear, they mean it is not useful. The answer to your question is the obvious *You read the documentation of that function and do what that documentation tells you to do.* so it isn't very useful. (not a down voter btw) – NathanOliver Sep 12 '18 at 20:47
  • Why are we assuming there's such documentation 100% of the time? That seems like quite a presumptuous assumption. Of course if there's documentation the question is trivial. – Apollys supports Monica Sep 12 '18 at 20:49
  • 3
    @Apollys If there is no such documentation, then who could possibly answer your question? The function becomes essentially unusable except by it's author. Everyone else would just have to guess if they should free, delete or not manage the pointed object's lifetime which is not an acceptable position to be in. – François Andrieux Sep 12 '18 at 20:50
  • Perhaps someone who knows more about C++ than I do. Obviously I asked this question because I wasn't certain that there wasn't an answer. It's also okay to say "that is impossible" - that too is an informative and useful answer. – Apollys supports Monica Sep 12 '18 at 20:51
  • @Apollys With well written modern code, it's safe to assume raw pointers aren't owning. But that's not true for legacy, poorly written or untrustworthy code. In those cases you have no choice but to read the documentation, or perhaps dive into that function's code to try to find out yourself. It's not a c++ knowledge thing, it varies from function to function. – François Andrieux Sep 12 '18 at 20:53
  • 3
    @Apollys Regarding your comment's edit, the answer is not "that is impossible". The answer is to read the documentation. My earlier comment on documentation was to explain that it's not presumptuous to assume there is documentation. If a function is being distributed, specially if it returns a pointer, it will have documentation. If it doesn't, it's distribution package is incomplete and it is, in effect, not usable without source diving. – François Andrieux Sep 12 '18 at 20:56

3 Answers3

8

How do I properly manage the memory allocated by this function?

You read the documentation of that function and do what that documentation tells you to do and when to do it, assuming there is anything to be done in the first place. It is important to remember that not all pointers refer to dynamic memory.

Example: The specification of strdup says:

Returns a pointer to a null-terminated byte string, which is a duplicate of the string pointed to by str1. The returned pointer must be passed to free to avoid a memory leak.

If no documentation is available, you can read the source code to see what the function does, and act accordingly. If the source isn't available either, then you're in a spot of bother, and might need to invest in a crystal ball.

Excluding standard functions, C APIs which acquire resources such as dynamic memory and return a handle (pointer in the case of dynamic memory) typically provide a corresponding function to release the resource. Example: SDL_CreateWindow and SDL_DestroyWindow.


would it be correct for me to infer that you are saying the following? In general, if you are given a raw pointer in C++, it is impossible to guarantee correct memory management of this pointer.

It is unclear what you mean. If you know how to manage the memory, then you can do it correctly. If you don't know how to manage the memory (or whether it needs to be managed by you at all), then you need a way to find the knowledge. There is no reasonable way in C++ to write the program in a way that would figure out (through introspection) how and when memory should be freed.

The type char* itself tells nothing about how the memory should be dealt with. Even the value of the pointer tells you nothing beyond if it is null, then it doesn't point to an object. If the pointer is non-null, then it might point to an object.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    To clarify the phrase "in a spot of bother" into more meaningful terminology, would it be correct for me to infer that you are saying the following? **In general, if you are given a raw pointer in C++, it is impossible to guarantee correct memory management of this pointer.** – Apollys supports Monica Sep 12 '18 at 21:05
  • 2
    @Apollys You can't even know if there's memory to be managed. – juanchopanza Sep 12 '18 at 21:06
  • That is a logical consequence of my bold statement above. But that is a good point as well. – Apollys supports Monica Sep 12 '18 at 21:08
  • 3
    @Apollys Perhaps what you are asking for is "It's not possible to, based strictly on the function prototype, safely deduce the lifetime management responsibilities a function caller has towards objects pointed by raw pointers returned by the function." – François Andrieux Sep 12 '18 at 21:13
  • @FrançoisAndrieux No, I meant what I wrote - it's simpler, clearer, more accurate, and grammatically correct. – Apollys supports Monica Sep 12 '18 at 21:23
  • 1
    @Apollys Then the answer is technically no, it would not be correct to infer that. It's possible to correctly manage memory pointer to by pointers. It just requires contextual information. Your statement could be made true if you included a clause about basing your guarantee *strictly* on the fact it's a raw pointer. Though I wouldn't suggest what you meant a second time. Maybe I'm misunderstanding a part of the original statement that implies no outside information. – François Andrieux Sep 12 '18 at 21:39
0
int *p = new int();
std::free(p);

That is uncorrect, it should be:

int *p = new int;
delete p;

or

int *p =  (int*) malloc(sizeof(int));
free(p);

Regarding to char* NotMyFunction();, wherever and however you allocate your char*, do not forget to to free or delete it. Do not loose your pointer, you will need it to release the memory.

char *NotMyFunction()
{
char* s = new char[n];
...
return s;
}

later:

char* nmf = NotMyFunction();
...
delete[] mfc;
0

I do not have thorough enough knowledge of C++ to say for myself, but based on what user2079303 has written in their answer, it seems that the theoretical answer to this question is that it is impossible. However, in practice there are many ways to approach this issue.


1. Check the documentation

In most cases, if you're developing software that uses a library, it should have documentation that provides this information. If not, you may want to question whether or not the benefits of using this library really outweigh the costs.

2. Check the source code

If you have access to the source code, you should be able to find out how the memory in question is being allocated. This is most likely more difficult than option 1, but it gives you a definite answer, rather than an answer that someone else wrote (which really should be correct, but doesn't have to be).

3. Use valgrind

Valgrind is a debugging utility tool, not a tool for proving correctness (correct me if I'm wrong), but in the absence of direct access to the resources we need to answer this question, it can be very useful. We can experiment with the following program, where we imagine that in our application we may not have access to the body of MysteryFunctionOne and MysteryFunctionTwo:

#include <iostream>

int* MysteryFunctionOne() {
  return new int();
}

int* MysteryFunctionTwo() {
  return (int *) malloc(sizeof(int));
}

int main() {
  int* p1 = MysteryFunctionOne();
  int* p2 = MysteryFunctionTwo();

  std::cout << std::hex << p1 << std::endl << p2 << std::endl;

  // For illustrative purposes, suppose we free both pointers incorrectly
  free(p1);
  delete p2;

  return 0;
}


If we compile and run the above program with valgrind program_name, we receive two important error messages, the first of which is:

Mismatched free() / delete / delete []
   at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
   by 0x400A2E: main (in <path/to/program>)
 Address 0x5ab6c80 is 0 bytes inside a block of size 4 alloc'd
   at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
   by 0x4009A3: MysteryFunctionOne() (in <path/to/program>)
   by 0x4009C8: main (in <path/to/program>)


This tells us that we have incorrectly used free in function main. The lower half of the error message even lets us know that operator new was used in MysteryFunctionOne(). Thus we know we need to delete the pointer returned by MysteryFunctionOne(), so we change

free(p1);

to

delete p1;

The second error message we receive is analogous, and from it we learn that we need to free(p2).

In this case, even without reading the source code or documentation, valgrind was able to guide us towards correct memory management. In general, this may not always be the case, but it is an option to consider in the absence of the source code or salient documentation.

Apollys supports Monica
  • 2,938
  • 1
  • 23
  • 33