Use of pointers

Feb 1, 2015 at 2:53pm
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 Feb 1, 2015 at 2:59pm
Feb 1, 2015 at 3:47pm
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
Feb 1, 2015 at 3:53pm
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 Feb 1, 2015 at 3:54pm
Feb 1, 2015 at 4:32pm
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 Feb 1, 2015 at 4:36pm
Feb 1, 2015 at 4:58pm
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)
Feb 1, 2015 at 5:39pm
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 Feb 1, 2015 at 5:39pm
Feb 1, 2015 at 9:44pm
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.
Feb 1, 2015 at 9:58pm
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.

Feb 2, 2015 at 6:37am
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.
Feb 2, 2015 at 7:36am
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
Feb 2, 2015 at 3:29pm
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?
Feb 2, 2015 at 3:48pm
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 Feb 2, 2015 at 3:49pm
Feb 2, 2015 at 5:07pm
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);
}
Feb 2, 2015 at 5:34pm
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.
Feb 2, 2015 at 6:59pm
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 Feb 2, 2015 at 6:59pm
Feb 2, 2015 at 7:26pm
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.
Feb 2, 2015 at 8:53pm
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?
Feb 2, 2015 at 9:05pm
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.
Feb 2, 2015 at 9:16pm
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!
Feb 2, 2015 at 9:33pm
Why?
Topic archived. No new replies allowed.