Use of pointers

When using pointers, when is it appropriate to call free?

In the following code, I allocate 4*4 bytes of memory, deallocate it and then allocate 4*7 bytes of memory.

1
2
3
4
5
6
7
8
9
10
int main()
{
	int* ptr;
	ptr = (int*)malloc(sizeof(int) * 4);
	printf("(1): %i\n", ptr);
	free(ptr); ptr = 0;
	ptr = (int*)malloc(sizeof(int) * 7);
	printf("(2): %i\n", ptr);
	getchar();
}


The output of this as I ran it is:
1
2
(1): 3960520
(2): 3960520


The two pointers point to the same address. My first question is "why does this happen?".

My second question is "what does free actually do here?". If I don't call free, will it lead to a memory leak?
Last edited on
free literally frees that memory so the system can use it again. and yes it can cause memory leaks if you dont call it. typically it only needs to be called when you are absolutely done with the pointer
My first question is "why does this happen?".
Nothing prevents system to give you memory area you just released back. You freed it. So if there are enough memory for another allocation, the same pointer could be returned.

My second question is "what does free actually do here?". If I don't call free, will it lead to a memory leak?
Yes, it would. Note that compiler might optimise two allocations and replace them with one: this will not change anything in visible behavior of the program, but will help with perfomance.
If you delete free, it would not be able to do so, and it will lead to (a) memory leak, (b) decreased perfomance.
Last edited on
Hm... Actually, the reason I ask is because I'm trying to implement a custom Array class in C++, and the code I'm using doesn't work when I have a two-dimensional array:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
template<typename T> class Pointer
{
public:
	T* ptr;

	Pointer() : ptr(0) {}
	Pointer(const Pointer<T>& other) : ptr(other.ptr) {}
	~Pointer() { this->Empty(); }

	void Empty()
	{
		if (ptr != 0)
		{
			free(ptr);
			ptr = 0;
		}
	}

	void AllocateMemory(uint16 amount) { ptr = (T*)malloc(sizeof(T) * amount); }
};

template<typename T> class Array
{
public:
	Pointer<T> p; uint16 size;

	Array() : size(0) {}
	Array(const Array<T>& other) { *this = other; }
	~Array() {}

	T& operator[](uint16 index) { return p.ptr[index]; }

	void operator=(const Array<T>& other)
	{
		if (p.ptr == other.p.ptr) { return; }

		if (other.size > 0)
		{
			p.Empty();
			size = other.size;
			p.AllocateMemory(size);
			for (uint16 i = 0; i < size; i++) { p.ptr[i] = other.p.ptr[i]; }
		}

		else { this->Empty(); }
	}

	void Empty()
	{
		p.Empty();
		size = 0;
	}

	void InsertEmptyValues(uint16 iFirst, uint16 inc)
	{
		if (iFirst > size) { return; }
		size += inc;
		Pointer<T> pCopy(p);
		p.AllocateMemory(size);
		for (uint16 i = 0; i < iFirst; i++) { p.ptr[i] = pCopy.ptr[i]; }
		for (uint16 i = iFirst + inc; i < size; i++) { p.ptr[i] = pCopy.ptr[i - inc]; }
	}

	void Insert(const T& value, uint16 index)
	{
		this->InsertEmptyValues(index, 1);
		p.ptr[index] = value;
	}
};

int main()
{
	Array<Array<int>> arr;
	arr.Insert(Array<int>(), 0);
	arr[0].Insert(42, 0);

	for (uint16 i = 0; i < arr.size; i++)
	{
		for (uint16 j = 0; j < arr[i].size; j++)
		{ printf("(%i, %i): %i\n", i, j, arr[i][j]); }
	}
}


From what I can gather, the code uses free on an already deallocated pointer. However, I can't figure out where it is - I debug it and I use breakpoints, but I just can't find the error. It seems to be crashing on line 74.

I have removed any code from the project that wasn't relevant, so it's essentially as short as it can be. If you have some time on your hands, I would really appreciate some help on this matter.
Last edited on
the code uses free on an already deallocated pointer
No, it uses free on pointer which was not allocated in first place:
In your insert function, memory at p.ptr[index] is uninitialized. So call to p.ptr[index] = value; will lead in the end to calling empty on Pointer class. Which compares some uninitialized memory with 0 (1/264 chance to get right) and then frees some random memory. This is why you should not use malloc() for classes. It does not work correctly. (You actually can use it with conjunction with placement new)
Also some other problems:
Array copy constructor (and assigment operator too) breaks if you are trying to copy empty array:
p will be initialized to nullptr, and size will get value of whatever happens to be in memory. Then assigment operator will be called but will not do anything, as pointers are compared equal. So you have array with null pointer and size, say, 600. Need I explain further which problems will follow?

Copy constructor of Pointer is dangerous: if you copy a pointer and let both original and copy go out of scope, you will have double delete problem.
Last edited on
Sorry for the late reply.

I don't quite understand what you mean...:

1. Why is the memory at p.ptr[index] uninitialized?
2. Does malloc not work for classes, then? What do I use instead, new[]?
3. Why would size not be equal to zero? In the default constructor of Array, it is initialized to zero - Array() : size(0) {}.

Sorry if I'm slow, I haven't worked much with C++ in combination with pointers before.
Why is the memory at p.ptr[index] uninitialized?
Because you did not initialize it.

Why would size not be equal to zero? In the default constructor of Array, it is initialized to zero
But you never call that constructor. You are calling copy constructor, and as you not using member init list, all members are default initialized. For builtin types that means not initialized at all.

Does malloc not work for classes, then? What do I use instead, new[]?
Malloc allocates memory. New[] allocates memory and initializes data. If you are going to use malloc, make sure that values are initialized properly, when they are used.

OK, so I got it working: either I replace malloc with new[], or I insert p.ptr = 0; into Array<T>::operator=.

However, I feel I don't quite understand:
1. Why is the copy constructor called? Does arr.Insert(Array<int>(), 0); create a copy?
2. When you say that new[] initializes data, what do you mean? Based on what does it initialize the data? On the default constructor?

Again, I apologize for my lack of knowledge, and thank you for your help.
Why is the copy constructor called?It does not in your code, but it is will be problem later. True problem in the assigment operator.

When you say that new[] initializes data, what do you mean?
I mean that it calls constructor for it,

Allocating memory is like buying a plot of land. Calling a constructor is like building a supermarket: you cannot shop in empty plot of land, and you cannot build on land you do not own. TO be able to use building properly you need to both buy plot of land and build it. New does both. malloc does only first step.

Based on what does it initialize the data? On the default constructor?
On whichever constructor you call. Usually it is default constructor.

either I replace malloc with new[], or I insert p.ptr = 0; into Array<T>::operator=.
It is more complex. You shall not use object (even assign to it) if it was not constructed. You need to allocate memory, construct object in-place using placement new and then you can use it. And do not forget to call destructors before you release memory.

You can use new[], but be cautionous, that it has slightly diferent meaning and might be not what you want (for example it will create variable just before you will overwrite it with assigment).


Example of your code rewritten to use uninitialize buffers and in-place construction:
http://coliru.stacked-crooked.com/a/f7d8999cb0534b60
1
2
3
4
5
void clear()
{
	for(std::size_t i = 0; i < size_; ++) { p[i].~T(); }
        /* ... */
}


Why is this done? Does free not do this automatically?
No. free just releases the memory. It does not destroy elements. You can test that with simple logging class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <memory>
#include <iostream>

struct C
{
     C() { std::clog << "Creating\n"  ; }
    ~C() { std::clog << "Destroying\n"; }
};

int main()
{
    C* c = (C*)malloc(sizeof(C)) ; //Allocate memory, does not calls constructor
    std::clog << "Memory allocated\n";
    new(c)C; //Call constructor manually using placement new
    std::clog << "Object created\n";
    free(c); //Free memory
    std::clog << "Memory freed\n";
} //Detructor was not called
Memory allocated
Creating
Object created
Memory freed
Note that destructor was not called by free (and constructor was not called by malloc), as free only handles memory, it does not know anything about classes and their invariants.

Last edited on
So, are the following comparisons accurate?:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Class
{
public:
	Class() { printf("Constructor called\n"); }
	~Class() { printf("Destructor called\n"); }
};

int main()
{
	Class* ptr;

	// Alternative 1
	ptr = new Class[2]; // calls ptr[i].Class() for each i in ptr?
	// ...
	delete[] ptr; // calls ptr[i].~Class() for each i in ptr?

	// Alternative 2
	ptr = (Class*)malloc(sizeof(Class) * 2);
	for (uint16 i = 0; i < 2; i++) { ptr[i] = Class(); }
	// ...
	for (uint16 i = 0; i < 2; i++) { ptr[i].~Class(); }
	free(ptr);
}
1
2
3
4
	// Alternative 1
	ptr = new Class[2]; // calls ptr[i].Class() for each i in ptr?
	// ...
	delete[] ptr; // calls ptr[i].~Class() for each i in ptr? 

The above is correct.


1
2
3
	for (uint16 i = 0; i < 2; i++) { ptr[i] = Class(); }  // <- not correct
	// ...
	for (uint16 i = 0; i < 2; i++) { ptr[i].~Class(); } // <- correct 



The ctor line here is not correct. You are creating a temporary object, then moving it to the object with the assignment operator. This is slightly different, as the assignment operator very well may assume the object has been properly constructed already (ie: try this with std::string or std::vector and it will likely crash the program).


The correct equivalent would be to use placement new to construct the object:

 
for (uint16 i = 0; i < 2; i++) { new(ptr[i]) Class(); }







Though really -- malloc and free are a C construct and have little/no place in C++... and you shouldn't be using them at all. Just use new/delete. Or better yet... use containers like vector and/or smart pointers to do the memory management so you don't ever have to worry about this issue, and you don't have to worry about memory leaks.
All right - I suppose I'll just use new and delete[], then. However, in the following code...:

1
2
int* ptr = new int;
delete[] ptr; // correct? 


Does this work correctly? I didn't encounter an error, but I'm not sure if it's allowed. The reason I used free in the first place was because it worked like delete and delete[].
Last edited on
Does this work correctly?
No. new should be paired with delete and new[] with delete[]

The reason I used free in the first place was because it worked like delete and delete[].
It did not.

delete works like that:
1
2
3
4
5
6
7
delete p; //p of type foo*
//rougtly equals to
{
    p->~foo();
    operator delete(p); //Does deallocation. Often implementted in terms:
  //free(p)
}
So delete works in two stages:
1) destroy object
2) deallocate memory.

delete[] deallocates several objects and then frees the memory. That second step is usually done the same way.

So when using free, you do only second part of proper deleting the objects. It if fine for trivial objects, as they lack constructors, dstructors and need to call them, but for anything more complex, you should call destructors.


To warn you, new[] does not really works well for dynamically growing arrays, as it initializes objects at the time of allocation which might be not what you want.
It is fine for small learning projects, but you will not be able to implement, say, vector properly using it.
So... Basically, when using dynamically growing data structures, I should be using ~T() and free, and malloc + whatever else I need to do, like initializing pointers and integers to zero?
Basically, when using dynamically growing data structures, I should be using ~T() and free, and malloc + whatever else I need to do
Depends on data structure in question, will you need uninitialized storage (usually yes for something vector-like) or it could be replaced with new expression (might be in case of lists).

You should not really use malloc. operator new (do not mix up with new expression or placement new) is a better choice in C++. And even better one for allocating storage will be using specialized allocators. I gave an example of using std::allocator earlier.
I prefer to use as few built-in C++ data types as possible; I use it more as "C with classes", and I want to write code which is easily portable to C. However, I'll look into it, since you say so. Thank you very much for the help!
Why?
Topic archived. No new replies allowed.