Ch5.14: memfunctions
Overview
C and C++ provide a family of low-level memory manipulation functions:
memcpy, memmove, memset,
memchr, and memcmp. These operate on raw bytes and
completely ignore types.
In this chapter, you will learn:
- how each function works
- why compilers treat them as intrinsics or “magic”
- why trivial
memcpyandmemmoveoften optimize to simplemovinstructions - how
memcpyis used for safe type punning - why calling these functions with
nullptrand non-zero size is undefined behavior - why you must check
size != 0before calling them - how to securely erase secrets using
::fast_io::secure_clear - where to read more about null-terminated byte strings
Required Header
All mem‑functions—memcpy, memmove,
memset, memcmp, and memchr—are declared in
the <cstring> header. You must include this header before using
any of them.
#include <cstring>
1. memcpy
memcpy copies raw bytes from one memory region to another.
int src{42};
int dst{0};
::std::memcpy(::std::addressof(dst),
::std::addressof(src),
sizeof(int));
After this call, dst contains the same bits as src.
1.1 memcpy ignores types
memcpy does not know or care what types are involved. It only copies
bytes. This is why it is safe for type punning.
1.2 memcpy is compiler magic
Modern compilers treat memcpy as an intrinsic. If the size is known
at compile time and small, the compiler often replaces it with a few
mov instructions.
memcpy(::std::addressof(dst), ::std::addressof(src), 4) → mov eax, [src]; mov [dst], eax
This is why memcpy is extremely fast.
1.3 memcpy for type punning
memcpy is the only pointer-safe way (besides ::std::bit_cast)
to reinterpret the bits of one object as another type.
float f{1.0f};
std::uint32_t u{};
::std::memcpy(::std::addressof(u),
::std::addressof(f),
sizeof(float)); // ✔ safe
This does not violate strict aliasing because no pointer of the wrong type is used.
1.4 memcpy and nullptr
Calling memcpy with a null pointer and a non-zero size is undefined behavior.
if (n != 0) {
::std::memcpy(dst, src, n);
}
Always check n != 0 before calling memcpy.
2. memmove
memmove is like memcpy but safe for overlapping memory.
::std::memmove(dst, src, n);
If the regions overlap, memcpy is undefined behavior, but
memmove handles it correctly.
2.1 memmove is also compiler magic
Compilers treat memmove as an intrinsic. If the compiler can prove
the regions do not overlap, it may optimize memmove into
memcpy or even simple mov instructions.
2.2 memmove and nullptr
Calling memmove with a null pointer and a non-zero size is undefined behavior.
if (n != 0) {
::std::memmove(dst, src, n);
}
3. memset
memset fills a block of memory with a byte value.
int x{};
::std::memset(::std::addressof(x), 0, sizeof(int));
This sets all bytes of x to zero.
3.1 memset is also compiler magic
If the compiler knows the size at compile time, it often replaces
memset with a sequence of mov instructions.
4. memchr
memchr searches for a byte value in a block of memory.
unsigned char buffer[8]{1,2,3,4,5,6,7,8};
void *p = ::std::memchr(buffer, 4, 8); // finds byte 4
memchr is also treated as a compiler intrinsic when possible.
5. memcmp
memcmp compares two blocks of memory byte-by-byte. It does not know
or care about types, encodings, or null terminators.
int a{42};
int b{42};
int result = ::std::memcmp(::std::addressof(a),
::std::addressof(b),
sizeof(int));
The return value is:
0if all bytes are equal< 0if the first differing byte inais less> 0if the first differing byte inais greater
5.1 memcmp ignores types
memcmp compares raw bytes. Padding bytes, endianness, and bit
patterns all affect the result.
5.2 memcmp is compiler magic
Compilers often optimize small, constant-size memcmp calls into a
few cmp instructions.
5.3 memcmp and nullptr
Calling memcmp with a null pointer and a non-zero size is undefined behavior.
if (n != 0) {
::std::memcmp(p, q, n);
}
6. Further reading: Null-terminated byte strings
Many C library functions operate on null-terminated byte strings. These are different from the raw byte functions in this chapter.
For a complete overview, see:
7. Securely clearing secrets with ::fast_io::secure_clear
When dealing with sensitive data such as passwords, cryptographic keys, or authentication material, it is important to securely erase the memory after use.
A common mistake is to use memset:
char secret[32]{};
// ... fill secret ...
::std::memset(secret, 0, sizeof(secret)); // ❌ may be optimized away
Compilers may remove this memset entirely because the memory is
never read afterward.
To solve this, ::fast_io provides:
::fast_io::secure_clear
This function is guaranteed to:
- actually overwrite the memory
- not be optimized away
- work on any trivially copyable type
- use the correct platform-specific secure clearing mechanism
Example
char secret[32]{};
// ... fill secret with sensitive data ...
::fast_io::secure_clear(secret, sizeof(secret)); // ✔ securely erased
This is the correct way to clear secrets in memory.
Key takeaways
memcpy,memmove,memset,memchr, andmemcmpoperate on raw bytes.- Compilers treat these functions as intrinsics or “magic”.
- Trivial
memcpyandmemmoveoften optimize to simplemovinstructions. memcpyis a safe way to type-pun bits (along with::std::bit_cast).- Always check
n != 0before calling these functions. - Calling them with
nullptrand non-zero size is undefined behavior. memmoveis the safe version ofmemcpyfor overlapping memory.::fast_io::secure_clearis the correct way to erase secrets.- See cppreference for null-terminated byte string functions.