《C 语言内存函数超详细讲解:从 memcpy 到 memcmp 的原理与实战》

发布于:2025-05-24 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

一. memcpy的使用和模拟实现

1.1 memcpy函数理解

1.2 memcpy使用示例

         1.3 memcpy模拟实现

二. memmove使用和模拟实现

2.1 memove函数理解

2.2 memove使用示例

2.3 memmove vs memcpy

2.4 memmove函数模拟实现

三. memset函数的使用

3.1 memset函数理解

3.1 memset使用示例

四. memcmp函数的使用

4.1 memcmp 函数理解

4.2 memcmp使用示例


一. memcpy的使用和模拟实现

1.1 memcpy函数理解

memcpy 是 C 标准库(<string.h>)中的一个函数,用于按字节复制内存块。它的原型如下:

void *memcpy(void *dest, const void *src, size_t n);

参数说明

参数

类型

说明

dest

void*

目标内存地址(复制到哪)

src

const void*

源内存地址(从哪复制)

n

size_t

要复制的字节数

返回值

  • 返回 dest 的指针(即目标内存地址)。

memcpy 的特点

  1. 不检查重叠(Undefined Behavior if Overlapping)

    • 如果 src 和 dest 的内存区域重叠,行为是未定义的(UB),可能导致数据错误。

    • 如果可能重叠,应该使用 memmove(它会正确处理重叠情况)。

  2. 按字节复制

    • 不管数据类型(intchar、结构体等),直接按字节复制。

  3. 不关心 '\0'(与 strcpy 不同)

    • strcpy 遇到 '\0' 停止,而 memcpy 严格复制 n 个字节。

1.2 memcpy使用示例

1. 复制数组

#include <stdio.h>
#include <string.h>

int main() {
    int src[5] = {1, 2, 3, 4, 5};
    int dest[5];

    memcpy(dest, src, sizeof(src));  // 复制整个数组

    for (int i = 0; i < 5; i++) {
        printf("%d ", dest[i]);  // 输出:1 2 3 4 5
    }

    return 0;
}

2. 复制结构体

#include <stdio.h>
#include <string.h>

typedef struct {
    int id;
    char name[20];
} Person;

int main() {
    Person p1 = {1, "Alice"};
    Person p2;

    memcpy(&p2, &p1, sizeof(Person));  // 复制整个结构体

    printf("ID: %d, Name: %s\n", p2.id, p2.name);  // 输出:ID: 1, Name: Alice

    return 0;
}

1.3 memcpy模拟实现

#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memcpy(void* dest, const void* source, size_t num)
{
    // 断言检查:确保 dest 和 source 不是 NULL
    assert(dest && source);

    // 保存目标地址的起始位置,用于返回
    void* ret = dest;

    // 逐字节复制,共复制 num 个字节
    while (num--)
    {
        // 将 source 的当前字节复制到 dest
        *(char*)dest = *(char*)source;

        // 移动 dest 和 source 指针到下一个字节
        dest = (char*)dest + 1;
        source = (char*)source + 1;
    }

    // 返回目标内存的起始地址
    return ret;
}

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20];
	my_memcpy(arr2, arr1, 40);
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

二. memmove使用和模拟实现

2.1 memove函数理解

memmove 是 C 标准库(<string.h>)中的一个函数,用于安全地复制内存块,即使源内存和目标内存有重叠也能正确处理。它的原型如下:

void *memmove(void *dest, const void *src, size_t n);

参数说明

参数

类型

说明

dest

void*

目标内存地址(复制到哪)

src

const void*

源内存地址(从哪复制)

n

size_t

要复制的字节数

返回值

  • 返回 dest 的指针(即目标内存地址)。


memmove 的特点

  1. 正确处理内存重叠

    • 如果 src 和 dest 的内存区域重叠memmove 会确保数据正确复制(不会像 memcpy 那样出现未定义行为)。

    • 实现方式:检查内存重叠情况,决定是从前往后复制还是从后往前复制。

  2. 按字节复制

    • 不管数据类型(intchar、结构体等),直接按字节复制。

  3. 不关心 '\0'(与 strcpy 不同)

    • strcpy 遇到 '\0' 停止,而 memmove 严格复制 n 个字节。

2.2 memove使用示例

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";

    // 把 "World!" 移动到前面(src 和 dest 重叠)
    memmove(str, str + 7, 6);  // 正确
    // memcpy(str, str + 7, 6);  // ❌ 未定义行为(UB)

    printf("%s\n", str);  // 输出:World! World!

    return 0;
}

2.3 memmove vs memcpy

函数 是否处理重叠 速度 适用场景
memcpy ❌ 不处理(UB) ⚡ 更快 确定不重叠时使用
memmove ✅ 安全处理 🐢 稍慢 可能重叠时使用

memmove 的实现原理

memmove 的底层实现通常会检查内存是否重叠:

  • 如果 dest < src:从前往后复制(避免覆盖未复制的数据)。

  • 如果 dest > src:从后往前复制(避免覆盖未复制的数据)。

2.4 memmove函数模拟实现

#include<stdio.h>   // 标准输入输出头文件
#include<assert.h>  // 断言头文件,用于调试检查

// 自定义的内存移动函数,功能类似于标准库的memmove
// 参数:
//   dest - 目标内存地址
//   src - 源内存地址
//   num - 要移动的字节数
// 返回值:返回目标内存地址
void* my_memmove(void* dest, const void* src, size_t num)
{
    // 使用断言确保dest和src都不是空指针
    assert(dest && src);
    
    // 保存目标地址的原始值,用于返回
    void* ret = dest;
    
    // 判断目标地址是否在源地址之前
    if (dest < src)
    {
        // 从前向后拷贝(适用于目标地址在源地址之前的情况)
        while (num--)
        {
            // 逐字节拷贝
            *(char*)dest = *(char*)src;
            // 移动指针到下一个字节
            dest = (char*)dest + 1;
            src = (char*)src + 1;
        }
    }
    else
    {
        // 从后向前拷贝(适用于目标地址在源地址之后的情况)
        // 这样可以防止重叠区域被覆盖
        while (num--)
        {
            // 从末尾开始拷贝
            *((char*)dest + num) = *((char*)src + num);
        }
    }
    
    // 返回目标地址
    return ret;
}

int main()
{
    // 测试数组
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    
    // 调用my_memmove函数:
    // 将arr+2开始的20个字节(即5个int)移动到arr开始的位置
    // 相当于把3,4,5,6,7移动到1,2,3,4,5的位置
    my_memmove(arr, arr + 2, 20);
    
    // 打印移动后的数组
    for (int i = 0; i < 10; i++)
    {
        printf("%d  ", arr[i]);
    }
    
    return 0;
}

关键实现细节

  • 通过比较 dest 和 src 的地址决定拷贝方向
  • 当 dest < src 时,从前向后拷贝(避免覆盖未拷贝的数据)
  • 否则,从后向前拷贝(同样是为了避免数据覆盖问题)
  • 使用 char* 指针进行逐字节操作

三. memset函数的使用

3.1 memset函数理解

memset 是 C/C++ 标准库中的一个内存操作函数,用于将一块内存区域填充为指定的值。

函数原型

void *memset(void *ptr, int value, size_t num);

参数说明

  • ptr: 指向要填充的内存块的指针

  • value: 要设置的值(以 int 形式传递,但实际使用时会被转换为 unsigned char)

  • num: 要填充的字节数

功能

memset 将从 ptr 开始的内存区域的前 num 个字节都设置为 value 的值。

常见用途

  1. 初始化数组为0:

    int arr[100];
    memset(arr, 0, sizeof(arr));
    
  2. 初始化结构体:

    struct MyStruct s;
    memset(&s, 0, sizeof(s));

3.1 memset使用示例

#include <stdio.h>
#include <string.h>
int main()
{
	char str[] = "hello world";
	memset(str, 'C', 6);
	printf(str);
	return 0;
}

我们来看下面这一串代码,判断能不能将arr的每个元素设置为1

int main()
{
	int arr1[] = { 0 };
	memset(arr1, 1, 40);
	return 0;
}

结果是不行,但是可以把一个数组里的都设置为0; 

四. memcmp函数的使用

4.1 memcmp 函数理解

memcmp 是 C/C++ 标准库中的一个内存比较函数,用于比较两块内存区域的内容。

函数原型

int memcmp(const void *ptr1, const void *ptr2, size_t num);

参数说明

  • ptr1: 指向第一个内存块的指针

  • ptr2: 指向第二个内存块的指针

  • num: 要比较的字节数

返回值

  • 负数ptr1 小于 ptr2 (按字典序)

  • 0ptr1 和 ptr2 相等

  • 正数ptr1 大于 ptr2 (按字典序)

功能

memcmp 逐字节比较 ptr1 和 ptr2 指向的内存区域的前 num 个字节,直到发现不匹配的字节或比较完所有字节。

常见用途

  1. 比较数组内容:

    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5] = {1, 2, 3, 4, 5};
    if (memcmp(arr1, arr2, sizeof(arr1)) == 0) {
        printf("Arrays are equal\n");
    }
    
  2. 比较结构体:

    struct Point { int x; int y; };
    struct Point p1 = {1, 2};
    struct Point p2 = {1, 2};
    if (memcmp(&p1, &p2, sizeof(struct Point)) == 0) {
        printf("Points are equal\n");
    }
    
  3. 比较字符串(不依赖null终止符):

    char str1[10] = "hello";
    char str2[10] = "hello";
    if (memcmp(str1, str2, 5) == 0) {
        printf("First 5 chars are equal\n");
    }
    

与 strcmp 的区别

特性

memcmp strcmp
比较方式 比较指定字节数 比较到遇到null终止符
安全性 更安全(指定长度) 需要确保字符串有终止符
性能 通常更快 需要检查每个字符是否为终止符
用途 任意内存区域比较 仅用于字符串比较

4.2 memcmp使用示例

#include <stdio.h>
#include <string.h>
int main()
{
	char buffer1[] = "ABCDEFG";
	char buffer2[] = "LOVEYOU";
	int n;
	n = memcmp(buffer1, buffer2, sizeof(buffer1));
	if (n > 0)
		printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
	else if (n < 0)
		printf("'%s' is less than '%s'.\n", buffer1, buffer2);
	else
		printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
	return 0;
}

结语:本篇文章内容就到此结束,继前面一篇文章后,在此篇文章中给大家继续分享了C语言内存函数中memcpy和memmove的使用和模拟实现,memset函数的使用,memcmp函数的使用等知识点,后续会继续给分享其它内容,如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。


往期回顾:

《C 语言字符串操作从入门到实战(上篇):字符分类、转换及strlen/strcpy等函数详解》
《C 语言字符串操作从入门到实战(下篇):strncpy/strncat/strstr 等函数原理与实现》

 


网站公告

今日签到

点亮在社区的每一天
去签到