C语言内存函数-

目录

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

[1.1 函数功能介绍](#1.1 函数功能介绍)

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

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

[2.1 函数功能介绍](#2.1 函数功能介绍)

[2.2 函数使用示例](#2.2 函数使用示例)

[2.3 模拟实现](#2.3 模拟实现)

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

[3.1 函数功能介绍](#3.1 函数功能介绍)

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

[4.1 函数功能介绍](#4.1 函数功能介绍)


1. memcpy使用和模拟实现

1.1 函数功能介绍

memcpy 函数用于从源内存地址的起始位置开始 ,复制指定字节数的数据到目标内存地址的起始位置 。它与 strcpy 的核心区别在于:strcpy 仅用于字符串复制,会以 '\0' 作为结束标志,而 memcpy 可用于复制任意类型的数据( 如整型、字符型、结构体等),复制的长度由第三个参数明确指定,不依赖结束标志

函数原型:void memcpy(void destination, const void* source, size_t num)**;

参数说明:

  • destination:目标内存地址,接收复制后的数据,注意该地址需可修改(不能是常量地址)。

  • source:源内存地址,提供需要复制的数据,用 const 修饰,确保源数据不被修改。

  • num:需要复制的字节数(size_t 是无符号整型类型)。

返回值:返回目标内存地址 destination(便于链式调用)。

注意事项:memcpy 函数默认不处理源内存块和目标内存块重叠的情况(若出现重叠,复制结果未定义)。

实例:

cpp 复制代码
#include <stdio.h>
#include <string.h> // memcpy 所在头文件

int main() {
    // 示例1:复制字符数组
    char src1[] = "hello world";
    char dest1[20] = {0};
    memcpy(dest1, src1, strlen(src1) + 1); // +1 是为了复制字符串结束标志 '\0'
    printf("复制字符数组结果:%s\n", dest1); // 输出:hello world

    // 示例2:复制整型数组
    int src2[] = {1, 2, 3, 4, 5};
    int dest2[5] = {0};
    memcpy(dest2, src2, sizeof(src2)); // sizeof(src2) 计算整个整型数组的字节数
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest2[i]); // 输出:1 2 3 4 5
    }
    printf("\n");

    return 0;
}

1.3 模拟实现

模拟实现思路:

  1. 参数合法性检查:确保 destination 和 source 不为 NULL(空指针),否则直接返回 NULL。

  2. 类型转换:由于函数参数是void* 类型(通用指针) ,无法直接解引用操作,需先将其转换为 char* 类型 (按字节操作,适配任意数据类型)。

  3. 循环复制:通过循环逐字节复制数据,循环次数为 num(复制的字节数),每复制一个字节后,两个指针均向后移动一位。

  4. 返回目标地址:复制完成后,返回 destination 地址。

cpp 复制代码
#include<stdio.h>
#include<string.h>
#include<assert.h>
void *my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		((char*)src)++;
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = {0};
	//memcpy--对源数组和目标数组之间的数据进行复制,从源数组的起始位置开始,
	// 复制n个字节到目标数组的起始位置,如果源数组和目标数组有重叠的部分,则覆盖目标数组的部分内存
	my_memcpy(arr2, arr1, 20);
	for (int i = 0; i < 20; i++) {
		printf("%d ", arr2[i]);
	}
	printf("\n");

	char arr3[20] = "hello,world";
	char arr4[40] = { 0 };
	void*p=my_memcpy(arr4, arr3, strlen(arr3) + 1); //+1 是为了复制字符串结束标志 '\0'
	printf("%s",(char*)p);

}

2. memmove使用和模拟实现

2.1 函数功能介绍

memmove 函数的功能与 memcpy 基本一致,均用于从源内存地址复制 num 个字节到目标内存地址。核心区别在于:memmove 支持源内存块和目标内存块重叠的情况,能够正确处理重叠区域的复制。

函数原型:void* memmove(void* destination, const void* source, size_t num);

参数说明:与 memcpy 完全相同(destination:目标地址、source:源地址、num:复制字节数)。

返回值:返回目标内存地址 destination。

内存重叠场景说明:当 source 地址范围与 destination 地址范围有重叠时,例如:char arr[] = "123456789"; 要将 arr[2] 开始的 5 个字节复制到 arr[0] 开始的位置,此时源区域和目标区域重叠,使用 memcpy 可能导致复制结果错误,而 memmove 可正确处理。

2.2 函数使用示例

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

int main() {
    // 测试内存重叠场景
    char arr[] = "123456789";
    printf("复制前:%s\n", arr); // 输出:123456789

    // 将 arr[2] 开始的 5 个字节("34567")复制到 arr[0] 开始的位置
    memmove(arr, arr + 2, 5);
    printf("复制后:%s\n", arr); // 输出:345676789

    // 测试非重叠场景(与 memcpy 效果一致)
    int src[] = { 1, 2, 3, 4, 5 };
    int dest[5] = { 0 };
    memmove(dest, src, sizeof(src));
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest[i]); // 输出:1 2 3 4 5
    }
    printf("\n");

    return 0;
}

2.3 模拟实现

模拟实现思路:

  1. 参数合法性检查:通过 assert 确保 dest 和 src 不为空指针。

  2. 处理内存重叠:分两种情况判断:

    1. 当目标地址 dest 在源地址 src 之前(dest < src):采用正向复制(从前往后逐字节复制),避免覆盖未复制的源数据。

    2. 当目标地址 dest 在源地址 src 之后(dest > src):采用反向复制(从后往前逐字节复制),同样避免覆盖未复制的源数据。

  3. 类型转换与复制:将 void* 转换为 char* 按字节操作,复制完成后返回 dest 地址。

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

// 修复1:const* src 语法错误 → const void* src
void* my_memmove(void* dest, const void* src, size_t num)
{
    assert(dest && src);
    void* ret = dest;
    if (dest < src)
    {
        while (num--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest + 1;
            ((char*)src)++;
        }
    }
    else
    {
        while (num--)
        {
            // 修复2:src侧少写取值符* → *((char*)src + num)
            *((char*)dest + num) = *((char*)src + num);
        }
    }

    return ret;
}

int main() {

    char arr[] = "123456789";
    printf("复制前:%s\n", arr); // 输出:123456789

    // dest>src(arr+2 > arr),走else分支从后往前拷贝
    my_memmove(arr + 2, arr, 5);
    // 实际正确输出:1212345789(而非你预期的345676789,原因见下文)
    printf("复制后:%s\n", arr);

    int src[] = { 1, 2, 3, 4, 5 ,7,8,9,10 };
    // 修复3:5*sizeof(int) 是正确的(仅拷贝5个int),保留你的写法
    my_memmove(src, src + 2, 5 * sizeof(int));
    // 修复4:循环i<10越界 → i<9(数组只有9个元素)
    for (int i = 0; i < 9; i++) {
        printf("%d ", src[i]); // 输出:3 4 5 7 8 7 8 9 10
    }
    printf("\n");

    return 0;
}

3. memset函数的使用

3.1 函数功能介绍

memset 函数用于将指定内存块的前 num 个字节设置为特定的字符 (注意:是按字节设置,不是按数据类型设置)。它常被用于初始化内存块,例如将数组初始化为 0、将字符串初始化为特定字符等。

函数原型:void* memset(void* ptr, int value, size_t num);

参数说明:

  • ptr:需要设置的内存块起始地址(可修改)。

  • value:需要设置的字符值(虽然参数类型是 int,但实际只使用低 8 位,即 ASCII 码范围)。

  • num:需要设置的字节数。

返回值:返回设置后的内存块起始地址 ptr。

注意事项:memset 是按字节设置的,因此不能直接用于设置非字符类型的数组为任意值 (例如:不能用 memset 将 int 数组设置为 1,因为每个 int 占 4 字节,会被设置为 0x01010101,即十进制的 16843009)。

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

int main() {
    // 示例1:初始化字符数组
    char arr1[20] = { 0 };
    memset(arr1, 'a', 5); // 将前 5 个字节设置为 'a'
    printf("字符数组初始化结果:%s\n", arr1); // 输出:aaaaa(后面为 '\0',不显示)

    // 示例2:清空字符数组(设置为 '\0')
    char arr2[] = "hello memset";
    memset(arr2, '\0', strlen(arr2)); // 将整个字符串设置为 '\0'
    printf("清空后字符数组:%s\n", arr2); // 输出空(无内容)

    // 示例3:初始化整型数组为 0(可行,因为 0 的 ASCII 码对应字节 0x00)
    int arr3[5] = { 1, 2, 3, 4, 5 };
    memset(arr3, 0, sizeof(arr3)); // 每个字节都设为 0,整个 int 就是 0
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr3[i]); // 输出:0 0 0 0 0
    }
    printf("\n");

    // 错误示例:试图将 int 数组设置为 1(结果不符合预期)
    int arr4[3] = { 0 };
    memset(arr4, 1, sizeof(arr4)); // 每个字节设为 1,int 为 0x01010101 = 16843009
    for (int i = 0; i < 3; i++) {
        printf("%d ", arr4[i]); // 输出:16843009 16843009 16843009
    }
    printf("\n");

    return 0;
}

4. memcmp函数的使用

4.1 函数功能介绍

memcmp 函数用于比较两个内存块的前 num 个字节内容是否相 同。它与 strcmp 的区别在于:strcmp 仅比较字符串(以 '\0' 结束),而 memcmp 可比较任意类型的内存块 (按字节逐一比较,不依赖结束标志)

函数原型:int memcmp(const void* ptr1, const void* ptr2, size_t num);

参数说明:

  • ptr1:第一个内存块的起始地址。

  • ptr2:第二个内存块的起始地址。

  • num:需要比较的字节数。

返回值(比较规则:按字节的 ASCII 码值大小比较,逐字节对比,直到找到不同字节或比较完 num 个字节):

  • 返回值 < 0:ptr1 指向的内存块中第一个不同字节的 ASCII 码值小于 ptr2 对应的字节。

  • 返回值 = 0:两个内存块的前 num 个字节完全相同。

  • 返回值 > 0:ptr1 指向的内存块中第一个不同字节的 ASCII 码值大于 ptr2 对应的字节。

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

int main() {
    // 示例1:比较字符数组(与 strcmp 效果一致,当比较长度覆盖整个字符串时)
    char arr1[] = "hello";
    char arr2[] = "helloworld";
    int ret1 = memcmp(arr1, arr2, 5); // 比较前 5 个字节
    printf("字符数组比较结果(前5字节):%d\n", ret1); // 输出:0(前5字节均为 "hello")

    int ret2 = memcmp(arr1, arr2, 6); // 比较前 6 个字节(arr1[5] 是 '\0',arr2[5] 是 'w')
    printf("字符数组比较结果(前6字节):%d\n", ret2); // 输出:-1('\0' ASCII 码小于 'w')

    // 示例2:比较整型数组
    int arr3[] = {1, 2, 3, 4}; // 十六进制:0x01 0x00 0x00 00, 0x02 0x00 0x00 00...
    int arr4[] = {1, 2, 4, 5};
    int ret3 = memcmp(arr3, arr4, 8); // 比较前 8 字节(即前 2 个 int,每个 int 4 字节)
    printf("整型数组比较结果(前2个元素):%d\n", ret3); // 输出:0(前 2 个元素相同)

    int ret4 = memcmp(arr3, arr4, 12); // 比较前 12 字节(前 3 个 int)
    printf("整型数组比较结果(前3个元素):%d\n", ret4); // 输出:-1(第3个元素 3 < 4,按字节比较时 0x03 < 0x04)

    return 0;
}
相关推荐
leaves falling2 小时前
c语言-扫雷游戏
c语言·单片机·游戏
至为芯3 小时前
IP6537至为芯支持双C口快充输出的45W降压SOC芯片
c语言·开发语言
小羊羊Python4 小时前
SoundMaze v1.0.1正式发布!
开发语言·c++
浩瀚地学4 小时前
【Java】JDK8的一些新特性
java·开发语言·经验分享·笔记·学习
l1t4 小时前
利用DeepSeek将python DLX求解数独程序格式化并改成3.x版本
开发语言·python·算法·数独
yugi9878385 小时前
基于遗传算法优化主动悬架模糊控制的Matlab实现
开发语言·matlab
moxiaoran57536 小时前
Go语言的错误处理
开发语言·后端·golang
yugi9878386 小时前
MATLAB的多层感知器(MLP)与极限学习机(ELM)实现
开发语言·matlab
楼田莉子6 小时前
Linux学习之磁盘与Ext系列文件
linux·运维·服务器·c语言·学习