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

目录

[一. memcpy的使用和模拟实现](#一. memcpy的使用和模拟实现)

[1.1 memcpy函数理解](#1.1 memcpy函数理解)

[1.2 memcpy使用示例](#1.2 memcpy使用示例)

[1.3 memcpy模拟实现](#1.3 memcpy模拟实现)

[二. memmove使用和模拟实现](#二. memmove使用和模拟实现)

[2.1 memove函数理解](#2.1 memove函数理解)

[2.2 memove使用示例](#2.2 memove使用示例)

[2.3 memmove vs memcpy](#2.3 memmove vs memcpy)

[2.4 memmove函数模拟实现](#2.4 memmove函数模拟实现)

[三. memset函数的使用](#三. memset函数的使用)

[3.1 memset函数理解](#3.1 memset函数理解)

[3.1 memset使用示例](#3.1 memset使用示例)

[四. memcmp函数的使用](#四. memcmp函数的使用)

[4.1 memcmp 函数理解](#4.1 memcmp 函数理解)

[4.2 memcmp使用示例](#4.2 memcmp使用示例)


一. memcpy的使用和模拟实现

1.1 memcpy函数理解

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

cpp 复制代码
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)

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

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

  2. 按字节复制

    • 不管数据类型(intchar、结构体等),直接按字节复制。
  3. 不关心 '\0'(与 strcpy 不同)

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

1.2 memcpy使用示例

1. 复制数组

cpp 复制代码
#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. 复制结构体

cpp 复制代码
#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模拟实现

cpp 复制代码
#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>)中的一个函数,用于安全地复制内存块,即使源内存和目标内存有重叠也能正确处理。它的原型如下:

cpp 复制代码
void *memmove(void *dest, const void *src, size_t n);

参数说明

参数 类型 说明
dest void* 目标内存地址(复制到哪)
src const void* 源内存地址(从哪复制)
n size_t 要复制的字节数

返回值

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

memmove 的特点

  1. 正确处理内存重叠

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

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

  2. 按字节复制

    • 不管数据类型(intchar、结构体等),直接按字节复制。
  3. 不关心 '\0'(与 strcpy 不同)

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

2.2 memove使用示例

cpp 复制代码
#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函数模拟实现

cpp 复制代码
#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;
}

关键实现细节

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

三. memset函数的使用

3.1 memset函数理解

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

函数原型

cpp 复制代码
void *memset(void *ptr, int value, size_t num);

参数说明

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

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

  • num: 要填充的字节数

功能

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

常见用途

  1. 初始化数组为0:

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

    cpp 复制代码
    struct MyStruct s;
    memset(&s, 0, sizeof(s));

3.1 memset使用示例

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

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

cpp 复制代码
int main()
{
	int arr1[] = { 0 };
	memset(arr1, 1, 40);
	return 0;
}

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

四. memcmp函数的使用

4.1 memcmp 函数理解

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

函数原型

cpp 复制代码
int memcmp(const void *ptr1, const void *ptr2, size_t num);

参数说明

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

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

  • num: 要比较的字节数

返回值

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

  • 0 : ptr1ptr2 相等

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

功能

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

常见用途

  1. 比较数组内容:

    cpp 复制代码
    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. 比较结构体:

    cpp 复制代码
    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终止符):

    cpp 复制代码
    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使用示例

cpp 复制代码
#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 等函数原理与实现》

相关推荐
PingdiGuo_guo4 分钟前
C++指针(三)
开发语言·c++
蟹至之3 小时前
【Java】异常的初步认识
java·开发语言·类和对象·异常
无垠的广袤3 小时前
【萤火工场GD32VW553-IOT开发板】流水灯
c++·单片机·嵌入式硬件·物联网
佩奇的技术笔记3 小时前
Python入门手册:Python基础语法
开发语言·python
学习使我变快乐3 小时前
C++:无序容器
数据结构·c++·算法
学习使我变快乐4 小时前
C++:STL
开发语言·c++
whoarethenext4 小时前
C/C++的OpenCV 进行轮廓提取
c语言·c++·opencv·轮廓提取
PingdiGuo_guo4 小时前
C++指针(二)
开发语言·c++
Magnetic_h5 小时前
【iOS】类结构分析
开发语言·笔记·学习·ios·objective-c
向哆哆5 小时前
Java 依赖管理工具:使用 Sonatype Nexus 管理项目依赖
java·开发语言