
Question:
I have a buffer in C with this code
buffer = malloc(sizeof(uint32_t)*CACHE_LEN*2);
How can I change this line to C++?
Is it better with malloc
or with new[]
?<br />
I cannot understand the meaning of this sizeof(uint32_t)
.
The line you mention will compile fine in C++, save for that fact that if buffer
is not a void *
, then you may need to cast the return value of malloc
in C++, which you don't need to do in C (and probably should not do).
EG:
uint32_t *buffer = (uint32_t *) malloc(sizeof(uint32_t)*CACHE_LEN*2);
However, you may wish to convert to a new
/ delete
paradigm; that will require more than just changing that line.
EG:
uint32_t *buffer = new uint32_t[CACHE_LEN*2];
...
delete[] buffer;
If you want to be more C++-like still, then use std::vector
or similar:
std::vector<uint32_t> buffer(CACHE_LEN*2);
Answer2:Assuming that CACHE_LEN
is indeed a macro that hides a value that is known at compile time:
auto buffer = make_unique<array<uint32_t, CACHE_LEN*2>>();
This has the fun property that you get pretty much all the advantages of modern C++ at just about zero overhead.
If you wish to stay somewhat closer to your C roots or CACHE_LEN
is not known at compile time, you can alternatively use, which removes some features that array
provides:
auto buffer = make_unique<uint32_t[]>(CACHE_LEN*2);
Finally, if you want to go all the other way, you can use a fully dynamic array (vector
in C++ lingo):
auto buffer = std::vector<uint32_t>(CACHE_LEN*2);
All of the above solutions ensure that resource management is being done by the scope of buffer
. The solutions that use make_unique
can (more or less) easily be changed to use shared pointers instead.
To stay completely old-school, std::malloc
and std::free
exist in C++ as well, but malloc
requires casting its result to the proper pointer type. They can also be replaced with new uint32_t[CACHE_LEN*2]
and delete[] buffer
.
P.S.:
The meaning of the sizeof(uint32_t)
is simply to account for the fact that malloc
does not allocate a number of elements, but rather a number of bytes - and most types require more than 1 byte per element.
std::vector<uint32_t> v(CACHE_LEN * 2);
sizeof(type)
return the size in chars of the type of the expression passed. For a uint32_t
it would be 4.
sizeof() gives the size in byte of the type given between the parenthesis. The type takes sizeof() in memory. This you need to know when you allocate memory to store data in. The CACHE_LEN * 2
depends solely on your usage.
If you need to store five times a pair of uint32_t you use:
malloc( sizeof( uint32_t ) * 2 * 5 );
malloc
will allocate a fixed size of memory. This means you can't change the size.
The new/delete is equivalent to malloc/free but in C++. If you use new[] you should use delete[] also.
char buf = new char[ 10 ];
delete[] buf;
The delete[] goes where the life cycle of the allocated memory has ended. If you create it within a function, then the delete[] should be there too. Or you get memory leaks. If you have it at the global scope of a class and you allocate the memory in the constructor, it is an assumption to delete[] the memory in the destructor but is usually the case. It depends on why and when you need the memory. One thing is for sure, if you allocate you need to deallocate (new[]/delete[]).
<blockquote>Paolo Bolzoni mentioned the use of std::vector
</blockquote>That's perfectly fine and in most cases very easy to use. If you need to optimize your code for speed, having a fixed size memory block would be faster then a std::vector. It depends on your needs but also your preference.
Answer5:
<blockquote>
buffer = malloc(sizeof(uint32_t)*CACHE_LEN*2);
</blockquote>
This can be <em>"translated"</em> in C++ in several ways.
But, first it must be clear what this does in C, since you wrote in your question that it's not clear for you the meaning of sizeof(uint32_t)
.
Using malloc()
, you can dynamically (i.e. at <em>run-time</em>) allocate some <strong>contiguous</strong> space from the <em>heap</em>.<br />
The parameter you pass to malloc
is the <em>size</em> of the requested space, <strong>in bytes</strong>.<br />
The code you posted seems to allocate CACHE_LEN*2
count of uint32_t
elements. Since malloc
expects its size parameter to be measured <em>in bytes</em>, you have to multiply CACHE_LEN*2
(the <em>"count"</em> factor) by the size, in bytes, of each element, i.e. of a uint32_t
, which is sizeof(uint32_t)
.
So, you have:
<ul><li>element type:uint32_t
</li>
<li>element count: CACHE_LEN*2
</li>
<li>size of each element, in bytes: sizeof(uint32_t)
</li>
</ul>So:
<blockquote><em>Total size in bytes</em> for malloc()
= sizeof(uint32_t) * CACHE_LEN*2
Now that this malloc()
call should be clear, let's move forward to the C++ equivalent.
The most immediate "mapping" is from C's malloc()
to C++'s <strong>new[]
</strong>.
Note that, while malloc
requires the size to be expressed in <em>bytes</em>, when you call new[]
you can just specify the <strong>element count</strong> (<em>not</em> the <em>raw</em> byte count).
So, a C++ code using new[]
would be something like this:
// Allocate (CACHE_LEN*2) elements of type uint32_t
uint32_t* buffer = new uint32_t[CACHE_LEN*2];
When you allocate memory using malloc()
, you have to free it using free()
.<br />
The C++ counterpart of new[]
is <strong>delete[]
</strong>, e.g.:
// Release buffer memory allocated with new[]
delete[] buffer;
// Avoid dangling references
buffer = nullptr;
But there's more. When <strong>memory allocation fails</strong>, malloc
just returns NULL
. Instead, C++'s new[]
<strong>throws an exception</strong> in case of allocation failure. This exception is of type std::bad_alloc
. You can catch that somewhare in your code to handle the memory allocation failure case.
If you want a behavior similar to malloc
, with failed allocations returning a nullptr
pointer, you can use the <strong>std::nothrow
</strong> constant with new[]
, e.g.:
buffer = new (std::nothrow) uint32_t[CACHE_LEN*2];
// On allocation failure, just returns nullptr
// (does *not* throw an exception).
<hr />However, in <em>modern C++</em> code, you may want to use a convenient container class to store your contiguous buffer memory. A very common candidate for that is <strong>std::vector
</strong>.
std::vector
is a class template: you specify the type of the elements as template argument, and you can specify the element count as a constructor parameter, e.g.:
#include <vector> // For std::vector
....
std::vector<uint32_t> buffer(CACHE_LEN*2);
Since the default memory allocator for vector
uses new[]
for allocating memory, in case of allocation failure the above constructor will throw a std::bad_alloc
C++ exception.
Note that the memory allocated by a vector
is <strong>automatically</strong> released by the vector's <em>destructor</em>. So you don't have to manually release that memory. This helps writing code that is <em>structurally incapable of leaking memory</em> (and other resources).
You can access the items in the vector using its overloaded operator[]
, just like an ordinary simple raw array (e.g. buffer[i]
accesses the i-th item in the buffer; note that indexes are 0
-based).
If you want a pointer to the beginning of the vector (which is the strict equivalent of your buffer
pointer), you can call vector
's <strong>data()</strong> method, e.g.:
// Points to the beginning of vector memory
uint32_t* ptr = buffer.data();
// ...use ptr...
Answer6:With C++
, malloc
become new
/new[]
and free
become delete
/delete[]
.