Skip to Content
Course content

99: Memory Functions: memcpy, memmove, memset, memcmp

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen a lot of developers—even some with years of experience—treat memcpy as a universal tool for moving bytes around. The most dangerous misconception I encounter is the belief that memcpy is safe to use when you're shifting data within the same array. You might think, "It's just copying bytes from one index to another in the same block of memory, how could that possibly go wrong?"

Let's look at why that's a recipe for a nightmare. Imagine you have a buffer and you want to shift everything one position to the left to delete the first element:

char data[] = {1, 2, 3, 4, 5};
// We want to move data[1...4] into data[0...3]
memcpy(data, data + 1, 4); 

On some architectures, this might work. On others, it will corrupt your data. Why? Because memcpy is allowed to copy in any direction it wants—forward or backward—to optimize for speed. If it starts copying from the end of the source range and moves backward, you're fine. But if it copies forward, it might overwrite the source byte before it ever gets a chance to copy it to the destination. It's called "undefined behavior," which is C's way of saying, "Your program might crash, or it might silently lie to you."

Stop using memcpy for overlapping regions; use memmove

When your source and destination buffers overlap, you must use memmove. It's slightly slower in some implementations because it effectively checks for overlap and decides whether to copy forward or backward to ensure no data is overwritten prematurely. I always tell my juniors: if there is even a 1% chance the buffers overlap, just use memmove. The performance hit is negligible compared to the hours you'll spend debugging a memory corruption bug.

char data[] = {1, 2, 3, 4, 5};
// This is the safe way to shift data within the same array
memmove(data, data + 1, 4); 

memset is for more than just zeroing out arrays

You'll see memset used constantly to zero out a struct or an array. That's its most common use case, but remember that it operates on bytes. This is where people get tripped up when working with integers. If you try to set an int array to 1 using memset, you won't get a bunch of 1s; you'll get a bunch of very large, strange numbers because memset fills every single byte of the integer with the value 1.

Use memset when you need to initialize a block of memory to a specific byte value (usually 0), or when you're clearing a buffer before filling it with network data:

struct UserProfile profile;
memset(&profile, 0, sizeof(profile)); // Clean slate, no garbage values

memcmp treats your data as a raw binary blob, not a string

Learners often confuse memcmp with strcmp. The critical difference is that strcmp stops the moment it hits a null terminator (\0). memcmp doesn't care about nulls; it just compares exactly N bytes. This makes it indispensable for comparing structs or binary packets where a zero byte is a valid piece of data and not a "stop" sign.

I personally prefer memcmp when I'm implementing a cache or a lookup table for binary keys. Just remember: you must be careful about "padding bytes" in structs. C compilers often insert invisible gaps between fields to align memory, and those gaps can contain random garbage. If you memcmp two structs that have the same field values but different garbage in their padding, memcmp will tell you they are different. To avoid this, memset your structs to zero before filling them.




📋 Practical Task

Implementing a Packet Buffer Shift

You are building a simple network packet processor. You have a fixed-size buffer containing a packet. The packet starts with a 4-byte "Header" that needs to be removed, shifting the remaining "Payload" to the very front of the buffer to make room for new data.

Your Task: Write a function void strip_header(unsigned char *buffer, size_t payload_size) that moves the payload (starting at index 4) to the start of the buffer (index 0) and then uses memset to clear the remaining 4 bytes at the end of the buffer to avoid leaving stale data.

Requirements:

  • Use memmove to handle the shift (since the source and destination overlap).
  • Use memset to zero out the trailing 4 bytes.
  • The function should handle a buffer where the payload size is passed as an argument.
// Example usage for your test:
unsigned char packet[] = {0xAA, 0xBB, 0xCC, 0xDD, 0x01, 0x02, 0x03};
size_t payload_len = 3; 
strip_header(packet, payload_len);
// Expected result: {0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.