C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
99: Memory Functions: memcpy, memmove, memset, memcmp
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
memmoveto handle the shift (since the source and destination overlap). - Use
memsetto 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}
There are no comments for now.