内存函数的使用和实现

目录

摘要:

一:memcpy

1:memcpy的使用

①:完整复制整形数组

②:部分复制整形数组

③:完整复制字符数组

④:部分复制字符数组

2:memcpy的实现

二:memmove

1:memmove的使用

①:不重叠

[②: 重叠情况1](#②: 重叠情况1)

[③: 重叠情况2](#③: 重叠情况2)

2:memmove的实现

①:情况分析

②:规律总结

③:实现代码

三:memset

1:memset的使用

①:清零数组

②:设置字符串的某一部分

四:memcmp

1:memcmp的使用

①:比较部分元素

②:比较小大写

[五:memcmp 对比 strcmp](#五:memcmp 对比 strcmp)


摘要:

介绍4中内存函数,memcpy,memmove,memset,memcmp。并对其中的memcpy和memmove进行模拟实现;最后对比memcmp和strcmp...

一:memcpy

1:memcpy的使用

函数原型:

void * memcpy ( void * destination, const void * source, size_t num );

参数解释:

  • dest:目标内存地址的指针,即复制后的数据存放位置。

  • src:源内存地址的指针,即要被复制的数据来源。

  • n:要复制的字节数。

函数功能:

将数据从 src 指向的内存区域复制 n 个字节到 dest 指向的内存区域。

  • 字节逐一复制。

  • 不会在复制的时候遇到 \0 而停止(与字符串函数 strcpy 不同)。

  • 适用于任意数据类型(intfloat、结构体、数组等)。

返回值:

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

注意事项:

①:内存区域不能重叠,重叠则必须使用memmove

⚠️:对于重叠,谨记使用memmove!某些编译器上memcpy也可以处理重叠的,比如vs,但不意味着所有编译器的memcpy上都可以实现重叠拷贝!所以我们模拟实现memcpy的时候,也只需要针对不重叠的情况去实现。

②:复制'\0'不会停止,因为 memcpy 完全按字节复制,不关心内容

⚠️:所以对于字符数组,来说,如果复制的内容没有包含'\0'结束符,则需要自己手动加上

使用示例:

①:完整复制整形数组

cpp 复制代码
#include <stdio.h>
#include <string.h>

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

    memcpy(dest, src, sizeof(src));

    printf("整型数组完整复制:\n");
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest[i]);
    }
    printf("\n");

    return 0;
}

**解释:**sizeof(src)计算的就是src这个被复制的数组的大小字节数

②:部分复制整形数组

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main() {
	int src[] = { 10, 20, 30, 40, 50 };
	int dest[5] = { 0 };

	memcpy(dest, src, 3 * sizeof(int));

	printf("整型数组部分复制(只复制前3个):\n");
	for (int i = 0; i < 5; i++) {
		printf("%d ", dest[i]);
	}
	printf("\n");

	return 0;
}

**解释:**对不部分复制,往往采取sizeof计算出单个元素类型的大小,然后乘上数目

③:完整复制字符数组

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello";
    char dest[10];

    memcpy(dest, src, strlen(src) + 1);

    printf("字符数组完整复制:\n");
    printf("%s\n", dest);

    return 0;
}

**解释:**对于"Hello"来说strlen(src)返回值为5,但我们需要+1去把'\0'也复制

④:部分复制字符数组

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main() {
	char src[] = "Hello";
	char dest[10];

	memcpy(dest, src, 3);
	dest[3] = '\0';

	printf("字符数组部分复制(只复制前3个,手动加结束符):\n");
	printf("%s\n", dest);

	return 0;
}

**解释:**对于字符数组的部分复制,我们往往会在dest数组后面手动置'\0'

2:memcpy的实现

void * memcpy ( void * destination, const void * source, size_t num );

两个参数的类型都是void *,是因为memcpy身为一个内存函数,是以字节为单位进行操作的,所以适配于很多类型,不知道传过来的是什么类型,所以用void *接收,内部再强转为char*以字节为单位进行操作即可

所以我们模拟实现的思路就是强转为char*之后然后进行num次的逐字节复制

代码:

cpp 复制代码
#include <assert.h>

void *my_memcpy(void *dst, const void *src, size_t count)
{
    void *ret = dst;               // 保存原地址,用于返回
    
    assert(dst && src);            // 断言非空
    
    while (count--) {
        *(char *)dst = *(char *)src;   // 逐字节复制
        dst = (char *)dst + 1;         // 指针后移1字节
        src = (char *)src + 1;
    }
    
    return ret;
}

解释:

**①:**首先 肯定是需要用ret保存一下dst指针的,用于最后的返回,因为dst会在后面的代码中改变

**②:**断言检测dst和src指针,其一为空,则代表用于传参错误,直接报错

**③:**以count为循环次数,在while循环的内部将dst和src强转为char*进行逐字节的复制

二:memmove

1:memmove的使用

void * memmove ( void * destination, const void * source, size_t num );

参数解释:

  • dest:目标内存地址的指针,数据要复制到的位置

  • src:源内存地址的指针,数据来源的位置

  • n:要复制的字节数

函数功能:

src 指向的内存区域中的 n 个字节复制到 dest 指向的内存区域。

返回值:

返回 dest 的指针(目标内存区域的首地址)

注意事项

①:主要用于内存重叠场景

  • destsrc 有重叠时,memmove安全的选择

②:性能略低于 memcpy

  • 因为 memmove 需要判断是否重叠,可能牺牲少量性能

  • 不重叠时优先用 memcpy,重叠时用 memmove

使用示例:

**①:**不重叠

cpp 复制代码
#include <stdio.h>
#include <string.h>

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

    memmove(dest, src, sizeof(src));

    printf("整型数组不重叠复制:");
    for (int i = 0; i < 5; i++)
        printf("%d ", dest[i]);
    printf("\n");

    return 0;
}

**解释:**不重叠的情况下,是等同于memcpy的

②: 重叠情况1

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
	int arr[] = { 10, 20, 30, 40, 50 };

	// 将 [30,40,50] 移动到 [10,20,30] 的位置
	memmove(arr, arr + 2, 3 * sizeof(int));

	printf("整型数组向左重叠移动:");
	for (int i = 0; i < 5; i++)
		printf("%d ", arr[i]);
	printf("\n");

	return 0;
}

解释: 重叠情况1,就是指针src 小于 指针desr ,memmove可以应对

③: 重叠情况2

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
	int arr[] = { 10, 20, 30, 40, 50 };

	// 将 [10,20,30] 移动到 [20,30,40] 的位置
	memmove(arr + 2, arr, 3 * sizeof(int));

	printf("整型数组向右重叠移动:");
	for (int i = 0; i < 5; i++)
		printf("%d ", arr[i]);
	printf("\n");

	return 0;
}

解释: 重叠情况2,就是指针src 大于 指针desr ,memmove也可以应对

2:memmove的实现

void * memmove ( void * destination, const void * source, size_t num );

对于不重叠的情况,我们不需要去讨论src和dest的大小关系,都可以直接复制

我们知道,复制的方式无非就是从前往后复制 或 从后往前复制 这两种方法

对于重叠的情况,则需要根据src和dest的大小关系分开讨论,寻找适配的复制方法!

①:情况分析

a:src 大于 dest

当src 大于 dest的时候,如果从后往前赋值,则一定出错!按照8,7,6,5,4的顺序复制给5,4,3,2,1,无法得到4,5,6,7,8,6,7,8,9,10(正确)而是得到7,8,6,7,8,6,7,8,9,10(错误)

而从前往后赋值,则正确:

b:src 小于 dest

当src 小于 dest的时候,若是从前往后复制,就是按照4,5,6,7,8的顺序复制给6,7,8,9,10,无法得到1,2,3,4,5,4,5,6,7,8(正确),而是得到1,2,3,4,5,4,5,4,5,4(错误)

而从后往前赋值,则正确:

②:规律总结

总结:

一:重叠情况,若src>dest,则从前往后进行复制;若src<dest,则从后往前复制

二:不重叠情况,则任意选择

三:统一 src>dest,前往后复制,src<dest,后往前复制

更深层次的理解:

当src(源)<dest(目的)的时候,只能后往前!

因为src后部分和dest的前部分重叠,此时采取错误的前往后拷贝,则会让src后部分还没被拷贝的元素被更新赋值了,等到之后要拷贝src后部分的元素则肯定错

当src(源)>dest(目的)的时候,只能前往后!

因为src前部分和dest的后部分重叠,此时采取错误的后往前拷贝,则会让src前部分还没被拷贝的元素被更新赋值了,等到之后要拷贝src前部分的元素则肯定错

③:实现代码

cpp 复制代码
#include <assert.h>

void *my_memmove(void *dest, const void *src, size_t num)
{
    void *ret = dest;
    assert(dest && src);

    if (src > dest)
    {
        // 内存不重叠或 dest 在前:从前往后复制
        while (num--)
        {
            *(char *)dest = *(char *)src;
            dest = (char *)dest + 1;
            src = (char *)src + 1;
        }
    }
    else
    {
        // 内存重叠且 dest 在后:从后往前复制
        while (num--)
        {
            *((char *)dest + num) = *((char *)src + num);
        }
    }

    return ret;
}

解释:

**①:**首先 肯定是需要用ret保存一下dst指针的,用于最后的返回,因为dst会在后面的代码中改变

**②:**断言检测dst和src指针,其一为空,则代表用于传参错误,直接报错

**③:**判断情况,若为src > dest ,则采用前往后复制,esle采取后往前复制;

**④:**前往后复制就是类似memcpy的强转为char*之后,直接num次循环逐字节复制;而后往前复制,则强转char*之后,需要让指针取指向最后一个元素的最后一个字节,然后往前逐字节复制!而while (num--)这一步,不仅循环了num次控制次数,并且让num在第一个进入循环就减1了,此时直接让强转为char*的指针+num即可指向最后一个元素的最后一个字节

三:memset

1:memset的使用

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

参数解释:

  • ptr:要操作的内存区域起始地址

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

  • num:要设置的字节数

函数作用:

将从 ptr 开始的 num 个字节,每个字节都设置为 value 的值。

  • 按字节设置,而不是按元素类型设置

  • 常用于内存区域的初始化 (清零)或填充

返回值:

返回 ptr 的指针(即内存区域的首地址)

注意事项:

⚠️ 1:memset 按字节填充,不会自动保留或添加 '\0',如果覆盖了 '\0' 或忘记添加 '\0',字符串操作函数会出错

⚠️2:memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容,一般将整形数组置为全0,或者将字符串的某一部分置为相同的字符

使用示例:

①:清零数组

cpp 复制代码
#include <stdio.h>
#include <string.h>

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

    printf("清零前:");
    for (int i = 0; i < 5; i++)
        printf("%d ", arr[i]);
    printf("\n");

    // 将整个数组清零
    memset(arr, 0, sizeof(arr));

    printf("清零后:");
    for (int i = 0; i < 5; i++)
        printf("%d ", arr[i]);
    printf("\n");

    return 0;
}

②:设置字符串的某一部分

cpp 复制代码
#include <stdio.h>
#include <string.h>

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

	printf("修改前:%s\n", str);

	// 将第6个字符开始(World)的5个字符置为 '*'
	memset(str + 6, '*', 5);

	printf("修改后:%s\n", str);

	return 0;
}

四:memcmp

1:memcmp的使用

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

参数解释:

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

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

  • num:要比较的字节数

函数作用:

ptr1ptr2 指向的内存区域的前 num 个字节逐字节进行比较

  • 按字节比较,不是按元素类型比较

  • 比较的是内存中的二进制值(ASCII 码或数值)

  • 遇到第一个不同的字节就停止比较

返回值:

返回值 含义
0 两块内存的前 num 个字节完全相同
> 0 ptr1 的第1个不同字节的值大于 ptr2 对应字节
< 0 ptr1 的第1个不同字节的值小于 ptr2 对应字节

注意事项:

⚠️: 按字节比较,不是按类型,其次memcmp 不会因 '\0' 停止,会完整比较 num 个字节

使用示例:

**①:**比较部分元素

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    int arr1[] = { 1, 2, 3, 4, 5 };
    int arr2[] = { 1, 2, 3, 9, 9 };

    // 只比较前3个元素(12字节)
    int result = memcmp(arr1, arr2, 3 * sizeof(int));

    if (result == 0)
        printf("前3个元素相同\n");
    else
        printf("前3个元素不同\n");

    return 0;
}

②:比较小大写

同一个字母的大小写的ascll码值不同,所以memcmp比较的时候,当然不同

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
	char str1[] = "Hello";
	char str2[] = "hello";

	int result = memcmp(str1, str2, 5);

	if (result != 0)
		printf("'H'(72) vs 'h'(104),所以不相等\n");

	return 0;
}

五:memcmp 对比 strcmp

对比二者,肯定是以字符数组/字符串的角度去对比的

代码:

对比 str1[] = "Hello\0World" 和 str2[] = "Hello\0ABCD"

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    // 两个字符串中间都含有 '\0'
    char str1[] = "Hello\0World";
    char str2[] = "Hello\0ABCD";

    // ========== memcmp ==========
    // 比较前5字节:只到 'o',还没遇到 '\0'
    int r1 = memcmp(str1, str2, 5);
    printf("memcmp 比较5字节:%d(相同)\n", r1);

    // 比较前11字节:会越过中间的 '\0',比较后面的 'W' vs 'A'
    int r2 = memcmp(str1, str2, 11);
    printf("memcmp 比较11字节:%d('W' > 'A',正数)\n", r2);

    // ========== strcmp ==========
    // 遇到第一个 '\0' 就停止,认为前面 "Hello" 相同
    int r3 = strcmp(str1, str2);
    printf("strcmp:%d(遇 \\0 停止,认为相同)\n", r3);

    return 0;
}

解释:

**①:**strcmp会觉得两个字符串完全相同,因为strcmp遇到'\0'就停止,所以压根比较不到后半部分

**②:**而memcmp遇到'\0'不会停止,其完全对比完之后,认为两个字符数组不相同

对比项 memcmp strcmp
头文件 <string.h> <string.h>
停止条件 比较完 num 个字节 遇到 '\0' 就停止
参数 (ptr1, ptr2, num) (str1, str2)
适用场景 任意内存块(二进制数据) 字符串(文本数据)
遇到 '\0' 不停止,继续比较 立即停止
可比较的内容 可含 '\0' 的数据 不能含 '\0'(会误停)

📌 [ 作者 ] shylyly

📃 [ 首次发布 ] 2026.5.16

❌ [ 最新修改 ] 无

📜 [ 声明 ] 由于笔者水平有限,文中难免有疏漏或不妥之处,还望读者不吝赐教。

相关推荐
时空自由民.2 小时前
两轮平衡车控制系统
算法
吃好睡好便好2 小时前
Matlab中三种三维图的对比
开发语言·人工智能·学习·算法·matlab·信息可视化
代码改善世界2 小时前
【C++进阶】二叉搜索树
java·数据结构·c++
157092511342 小时前
回溯算法基础分享
算法·深度优先
脆皮炸鸡7552 小时前
进程通信----命名管道
linux·经验分享·笔记·算法·学习方法
如竟没有火炬2 小时前
至少有K个重复字符的最长子串
开发语言·数据结构·python·算法·leetcode·动态规划
想带你从多云到转晴2 小时前
优选算法---双指针
java·算法
小O的算法实验室3 小时前
2026年IEEE TSMC,基于Q学习平衡全局与局部搜索的防空资源分配问题进化算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
谙弆悕博士3 小时前
快速学C语言——第17章:多文件编程与头文件规范
c语言·开发语言·算法·学习方法·头文件·多文件编程