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:

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:

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:

cppreference: Null-terminated byte strings

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:

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