I am quite new at C++ and I really wanna learn how to work with allocating memory.
So I have created some utility functions. I am aware that C++ uses new
and delete
instead of malloc
and free
Do these functions cause any memory leakage? (They probably do since I am experiencing some bugs).
// Includes.
#include<stdio.h> // for alloc() and del().
// Allocate new memory.
template <class T>
T* memalloc(slong_t alloc_size) { //}, const char* path = __builtin_FILE(), int line = __builtin_LINE(), const char* func = __builtin_FUNCTION()) {
T* ptr;
// print("Allocated ", alloc_size, ": called from <Func>", func, "[", path, ":",line ,"]");
unsigned long n = (unsigned long) alloc_size;
ptr = (T* ) malloc((n + 1) * sizeof(T));
// ptr = new T[(unsigned long) alloc_size + 1];
return ptr;
}
// Check if memory is allocated.
// Will cause segmentation fault if x is unallocated [const T* x;], when x is [const T* x = ""] it will run.
template <class T>
bool memnull(const T* x) {
return x == NULL || ! *x;
}
// Delete allocated memory.
// Does not check if the memory is allocated.
template <class T>
void memdelete(T*& source) { //}, const char* path = __builtin_FILE(), int line = __builtin_LINE(), const char* func = __builtin_FUNCTION()) {
// if (source != NULL) {
// print("Deleting address", (address_t) source, "[", source, "]", ": called from <Func>", func, "[", path, ":",line ,"]");
free(source);
source = NULL;
// } else {
// print("NOT Deleting", (address_t) source, "[", source, "]", "from", line, func);
// }
}
// Memory equals.
template <class T>
bool memequals(const T* str1, const T* str2, slong_t size) {
if (size == 0) { return true; }
while (size-- > 0) { if (*str1++ != *str2++) { return false; }}
return true;
}
// Move memory.
// Produces segfault when "dest = source".
template <class T>
void memmove(T* dest, const T* source, slong_t size) {
if (size == 0) { return; }
const T* s = source;
if (dest < s) { while (size--) { *dest++ = *s++; }}
else {
const T* lasts = s + (size-1);
T* lastd = dest + (size-1);
while (size--) { *lastd-- = *lasts--; }
}
}
// Copy memory.
template <class T>
void memcopy(T* dest, const T* source, slong_t size) {
if (size == 0) { return; }
const T* s = source;
while (size--) { *dest++ = *s++; }
}
// Fill memory.
template <class T>
void memfill(T*& dest, T val, slong_t size) {
if (size == 0) { return; }
while (size-- > 0) { *dest++ = val; }
}
// Duplicate memory.
template <class T>
T* memduplc(T* source, slong_t size, slong_t new_alloc_size = 0) {
if (new_alloc_size == 0) { new_alloc_size = size; }
T* dest = memalloc<T>(new_alloc_size);
if (source != NULL) { memcopy(dest, source, size); }
return dest;
}
// Swap allocated memory.
// Only for move constructors.
template <class T>
void memswap(T*& dest, T*& source) {
dest = source;
source = NULL;
}
// Cast from const memory.
template <class T>
T* memcast(const T* source, slong_t size, slong_t new_alloc_size = 0) {
if (new_alloc_size == 0) { new_alloc_size = size; }
T* dest = memalloc<T>(new_alloc_size);
if (source != NULL) { memcopy(dest, source, size); }
return dest;
}
// Reallocate memory with a new size.
// Copies the content of "source".
template <class T>
void memrealloc(T*& source, slong_t size, slong_t new_alloc_size) {
T* dest = memalloc<T>(new_alloc_size);
if (source != NULL) {
memmove(dest, source, size);
source = NULL;
}
source = dest;
}
// Reverse memory.
template <class T>
T* memreverse(T* x, slong_t size, slong_t alloc_size = 0) {
if (alloc_size == 0) { alloc_size = size; }
T* arr = memalloc<T>(alloc_size);
if (size == 0) { return arr; }
slong_t li = 0;
for (double ri = size - 1; ri >= 0; --ri) { arr[li] = x[(slong_t)ri]; ++li; }
return arr;
}
template <class T>
const T* memreverse(const T* x, slong_t size, slong_t alloc_size = 0) {
if (alloc_size == 0) { alloc_size = size; }
T* arr = memalloc<T>(alloc_size);
if (size == 0) { return arr; }
slong_t li = 0;
for (double ri = size - 1; ri >= 0; --ri) { arr[li] = x[(slong_t)ri]; ++li; }
const T* carr = arr;
// if (size > 0 and arr) { memdelete(arr); }
return carr;
}
// Find the index of an item in an array.
// Does support negative index.
// Reverts "start" to "0" and "end" to "size" when the values are out of range.
template <class T>
slong_t memfind(const T* arr, T to_find, slong_t size, index_t start = 0, index_t end = 0) {
start = start.subscript(size);
if (start == npos) { start = 0; }
end = end.subscript(size);
if (end == npos) { end = size; }
for (slong_t i = start; i < end; ++i) {
if (arr[i] == to_find) { return i; }
}
return npos;
}
// Check if an array contains an item.
// Does support negative index.
// Reverts "start" to "0" and "end" to "size" when the values are out of range.
template <class T>
bool memcontains(const T* arr, T to_find, slong_t size, index_t start = 0, index_t end = 0) {
return memfind(arr, to_find, size, start, end) != npos;
}
// Slice memory.
// Does support negative index.
// Reverts "start" to "0" and "end" to "size" when the values are out of range.
template <class T>
slong_t memslice(T*& x, slong_t size, index_t start, index_t end = 0) {
start = start.subscript(size);
if (start == npos) { start = 0; }
end = end.subscript(size);
if (end == npos) { end = size; }
size = end - start;
T* sliced = memalloc<T>(size);
memmove(sliced, x + start, size);
x = sliced;
return size;
//
// slong_t index = 0;
// for (slong_t i = start; i < end; ++i) {
// sliced[index] = x[i];
// ++index;
// }
// x = sliced;
// sliced = NULL;
// return index;
// start = start.subscript(size);
// if (start == npos) { start = 0; }
// end = end.subscript(size);
// if (end == npos) { end = size; }
// size = end - start;
// T* sliced = memalloc<T>(size);
// for (slong_t i = start; i < end; ++i) {
// sliced[i] = x[(i - start)];
// }
// // if (!memnull(x)) { memdelete(x); }
// x = sliced;
// return (slong_t) size;
}
// Replace memory.
// Does support negative index.
// Resets "start" to "0" and "end" to "size" when the values are out of range.
template <class T>
void memreplace(T* source, T from, T to, slong_t size, index_t start = 0, index_t end = 0) {
if (from == to) { return ; }
start = start.subscript(size);
if (start == npos) { start = 0; }
end = end.subscript(size);
if (end == npos) { end = size; }
for (slong_t i = start; i < end; ++i) {
if (source[i] == from) { source[i] = to; }
}
}
// Shift error.
class ShiftError {
public: const char* message;
ShiftError(const char* x) { message = x; } };
// Fast memory shift.
// Resizes the memory with the specified "alloc_size".
// Parameter "end" must be "size + nshift".
// Produces segfaults when the values of "start" / "end" are out of range.
// Produces segfault when "nshift" is larger then "size" or smaller then "-size"
template <class T>
slong_t fastmemshift(T* source, int nshift, slong_t start, slong_t end, slong_t alloc_size) {
T* shifted = memalloc<T>(alloc_size);
// Shift to the right from start.
if (nshift > 0) {
memcopy(shifted, source, end);
for (slong_t i = end - 1 + nshift; i > start + (nshift - 1); i--) {
shifted[i] = shifted[i - nshift];
}
}
// Shift to the left from start.
else if (nshift < 0) {
memcopy(shifted, source, start);
for (slong_t i = start; i < end; ++i) {
shifted[i] = source[i - nshift];
}
}
// Assign.
if (!memnull(source)) { memdelete(source); }
source = shifted;
// Return new alloc size.
return alloc_size;
}
// Shift memory.
// Produces segfault when "nshift" is larger then "size" or smaller then "-size"
// Automatically resizes the array when required.
// A positive "nshift" value shifts the array to the right from the "start" index.
// Therefore the array's size will be larger.
// Thus "memshift("Hello", ..., 1, 1)" will return "Heello World!".
// A negative "nshift" value shifts the array to the left from the "start" index.
// Therefore the array's size will be smaller.
// Thus "memshift("Hello World!", ..., -1, 1)" will return "Hllo World!".
// Everything before index "start" and after index "end" remains the same.
// Shifts nothing when "start" is out of range.
// Automatically reverts "end" to "size" when the value is "0" / out of range.
// Best leave "end" "0", otherwise keep in mind that "end" must be "x + nshift" with x as your end.
// Optionally specify "alloc_size" to set the newly allocated size, automatically increases when the specified value is too small.
// Returns the newly allocated size of the memory.
template <class T>
slong_t memshift(T* source, slong_t size, int nshift, index_t start = 0, index_t end = 0, slong_t alloc_size = 0) {
start = start.subscript(size);
if (start == npos) {
if (alloc_size == 0) { return size; }
return alloc_size;
}
if (end > size + nshift) { end = size + nshift; }
else if (end == 0) {
end = start.subscript(size);
if (end == npos) { end = size + nshift; }
}
if (end > alloc_size) { alloc_size = end; }
else if (alloc_size == 0) { alloc_size = size; }
return fastmemshift(source, nshift, start, end, alloc_size);
}
// Pop an item.
// Supports negative index.
// Returns "T()" when the index is out of range.
template <class T>
T mempop(T* source, slong_t size, index_t index, slong_t alloc_size = 0) {
if (index == 0) {
T x = source[0];
++source;
return x;
}
if (alloc_size == 0) { alloc_size = size; }
index = index.subscript(size);
if (index == npos) { return T(); }
T x = source[index];
fastmemshift(source, -1, index, size - 1, alloc_size);
return x;
}
// Pop an item.
// Supports negative index.
// Returns "def" when the index is out of range.
template <class T>
T mempop(T* source, slong_t size, index_t index, T& def, slong_t alloc_size = 0) {
if (index == 0) {
T x = source[0];
++source;
return x;
}
if (alloc_size == 0) { alloc_size = size; }
index = index.subscript(size);
if (index == npos) { return def; }
T x = source[index];
fastmemshift(source, -1, index, size - 1, alloc_size);
return x;
}
I am using macOS with compiler clang++
.
<stdio.h>
is a C header, not a C++ header. And there are some types in there which have no definition (slong_t
,index_t
). \$\endgroup\$<stdio.h>
is a standard header in C++. It’s been deprecated since 1998, but will officially never be removed. \$\endgroup\$#include <stdio.h>
lets you do both.)”. No, this is incorrect, too.<stdio.h>
ONLY guaranteesprintf()
… NOTstd::printf()
. An implementation may providestd::printf()
in<stdio.h>
… but they don’t have to. (Similarly, an implementation MAY provideprintf()
in<cstdio>
… but they don’t have to. They must providestd::printf()
in<cstdio>
, though.) \$\endgroup\$