C语言内存函数详解

前言

本篇博客主要介绍常用的4个内存操作函数:

1.不重叠内存拷贝函数 -- memcpy()

2.重叠内存拷贝函数 -- memmove()

3.内存比较函数 -- memcmp()

4.内存填充函数 -- memset()

使用这4个函数均需要包含头文件string.h

文章目录

  • 前言
    1. 不重叠内存拷贝函数 -- `memcpy()`
    • 1.1 函数功能介绍
    • 1.2 参数及返回值介绍
    • 1.3 使用`memcpy`拷贝各类型数据
      • 1.3.1 代码
      • 1.3.2 输出结果
      • 1.3.3 解释
    • 1.4 使用细节
    • 1.5 `memcpy`函数的模拟实现
    1. 重叠内存拷贝函数 -- `memmove()`
    • 2.1 函数功能介绍
    • 2.2 `memmove()`的实现原理
      • 2.2.1 第一种重叠形式
      • 2.2.2 第二种重叠形式
      • 2.2.3 总结
    • 2.3 模拟实现`memmove()`
  • 3.内存比较函数 -- `memcmp()`
    • 3.1 函数功能介绍
    • 3.2 代码举例
      • 3.2.1 比较整型数据
      • 3.2.2 比较字符串(对比`strncmp`)
    1. 内存填充函数 -- `memset()`
    • 4.1 初始化`malloc`开辟的数组
    • 4.2 初始化结构体变量
    • 4.3 初始化结构体数组

1. 不重叠内存拷贝函数 -- memcpy()

1.1 函数功能介绍

函数memcpysource指向的内存空间的前num个字节的数据复制到destination指向的空间中。

1.2 参数及返回值介绍

函数原型:

c 复制代码
void* memcpy ( void * destination, const void * source, size_t num );
  • 参数1:destination -- 指向用于存储复制内容的目标空间,类型强制转换为 void* 指针。

  • 参数2:source -- 指向要复制的数据源,类型强制转换为 void* 指针,const关键字保证数据源不会被修改。

  • 参数3:num -- 要被复制的字节数。

  • 返回值:目标空间地址destination,类型为void*

1.3 使用memcpy拷贝各类型数据

1.3.1 代码

(来自网站cplusplus)

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

struct {
    char name[40];
    int age;
} person, person_copy;

int main()
{
    char myname[] = "张三";

    /* 1. 使用 memcpy 拷贝 字符串 */
    memcpy(person.name, myname, strlen(myname) + 1);
    person.age = 46;

    /* 2. 使用 memcpy 拷贝 结构体数据: */
    memcpy(&person_copy, &person, sizeof(person));

    printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);

    /* 3. 使用 memcpy 拷贝 数组数据 */
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr_copy[10] = { 0 };

    memcpy(arr_copy, arr, sizeof(arr));
	
	printf("arr_copy: ");
    for (int i = 0; i < 10; i++)
        printf("%d ", arr_copy[i]);

    return 0;
}

1.3.2 输出结果

c 复制代码
person_copy: 张三, 46
arr_copy: 1 2 3 4 5 6 7 8 9 10

1.3.3 解释

  1. memcpy(person.name, myname, strlen(myname) + 1);
    • 第一个参数person.name是指向待放数据数组首元素的指针变量。
    • 第二个参数myname是指向待拷贝的源头数组首元素的指针变量。
    • 第三个参数为表达式strlen(myname) + 1strlen(myname)表示字符串长度(单位:字节),+1表示将'\0'拷贝过去
  2. memcpy(&person_copy, &person, sizeof(person));
    • 第一个参数&person_copy是待放拷贝数据的结构体空间首地址。
    • 第二个参数&person是待拷贝的源头结构体空间首地址。
    • 第三个参数sizeof(person),表示将结构体person的全部内容拷贝到结构体person_copy(单位:字节)。
  3. memcpy(arr_copy, arr, sizeof(arr));
    • 第一个参数arr_copy是待放拷贝数据的数组首元素地址。
    • 第二个参数arr是待拷贝的源头数据数组首元素地址。
    • 第三个参数sizeof(arr)表示拷贝内容大小为40个字节

1.4 使用细节

  1. 这个函数在遇到 '\0' 的时候并不会停下来。
  1. 前面讲到过,memcpy是使用在不重叠的内存块之间的拷贝,但是,如果sourcedestination有任何的重叠,那么拷贝的结果又当如何------答案是不知道!
    C语言官方文档中未对该状况做出明确定义,所以具体情况得看编译器提供的库函数是如何实现的。

假设使用memcpy进行重叠内存拷贝的预期结果:

实际运行的结果:

由于内存重叠,在拷贝过程中内存中的数据会受到改变,因此实际结果并非我们预期说想的结果。

  1. VS里面memcpy超额完成了功能,它能够进行重叠内存的拷贝。也就是使其拥有memmove的功能至于memmove又有什么功能,别急,下一个介绍的函数就是它。

1.5 memcpy函数的模拟实现

c 复制代码
#include <assert.h>
void* myMemcpy(void* dest, const void* src, size_t num)
{
    assert(dest && src);

    for (size_t i = 0; i < num; i++)
        ((char*)dest)[i] = ((char*)src)[i];

    return dest;
}

2. 重叠内存拷贝函数 -- memmove()

2.1 函数功能介绍

c 复制代码
void * memmove ( void * destination, const void * source, size_t num );
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

2.2 memmove()的实现原理

memmove很好地解决了memcpy在拷贝过程中源内存空间内容因被覆盖而被改变的问题

上面已经把为何会导致出现问题的原因讲的很详细了,所以这里的重点就放在memmove如何解决这个问题上来------在模拟实现memmove()的过程中进行解答

2.2.1 第一种重叠形式

从上面的拷贝原理图中看到,从src指向空间的数据被从低地址向高地址拷贝(从前向后拷贝)是行不通的。但是,拷贝的方式又不是只有这一种,为什么不试试从后向前拷贝呢?

图解如下:

拷贝过程中,即使内存中的数据本身被覆盖了,但是源数据本身该被拷贝的数据却并没有没改变。

2.2.2 第二种重叠形式

解决了上面第一种内存重叠情况的拷贝后,当出现遇到第二种内存重叠情况时,仔细分析一下不难发现这种情况下,更适合从前向后拷贝。

图解如下:

2.2.3 总结

以src为界,

dest < src时, 源空间数据从前向后拷贝。

dest >= src时,源空间数据从后向前靠拷贝。

2.3 模拟实现memmove()

c 复制代码
include <assert.h>
void* myMemmove(void* dest, const void* src, size_t num)
{
    assert(dest && src);

    if (dest < src)
    {
        for (size_t i = 0; i < num; i++)
            ((char*)dest)[i] = ((char*)src)[i];
    }
    else
    {
        while(num--)
            ((char*)dest)[num] = ((char*)src)[num];
    }
    return dest;
}

3.内存比较函数 -- memcmp()

函数原型

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

3.1 函数功能介绍

字节为单位,比较内存空间ptr1ptr2的前num个字节的数据大小。

  • ptr1指向内存块1,类型为void*,可接受任何类型数据
  • ptr2指向内存块2,类型为void*,可接受任何类型数据
  • num表示待比较字节数
  • 返回值规则如下(图片来自cplusplus):

3.2 代码举例

3.2.1 比较整型数据

第一次比较1个字节,arr2指向的第1个字节 > arr1指向的第1个字节,返回负数。

第二次预计比较21个字节,但是在第一个字节比较完毕时已返回比较结果

3.2.2 比较字符串(对比strncmp

从上面的代码例子可以看出,memcmpstrncmp二者之间的功能在大致上是相似的,但是,当字符> 串长度小于num时,strncmp遇到'\0'会结束,而memcmp不会,它继续比较,直到比较完num个字节

4. 内存填充函数 -- memset()

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

一言蔽之,函数将ptr指向的内存空间的前num个字节设定为默认值value,最后返回ptr

这个函数比较适合用来初始化一些书写不太方便的变量或者数组。

4.1 初始化malloc开辟的数组

两种初始化方式,使用memset比较方便

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

int main()
{
    int* arr1 = (int*)malloc(sizeof(int) * 10);
    int* arr2 = (int*)malloc(sizeof(int) * 10);
    
    //arr1初始化为全0
    memset(arr1, 0, sizeof(int) * 10);
    
    //arr2初始化为全0
    for (int i = 0; i < 10; i++)
        arr2[i] = 0;

    return 0;
}

4.2 初始化结构体变量

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

struct student{
	int ID;
	char name[20];
};

int main(){
	struct student stu;
	memset(&stu, 0, sizeof(stu));
	printf("%d\t%s", stu.ID, stu.name);
	return 0;
}

4.3 初始化结构体数组

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

struct student{
	int ID;
	char name[20];
};

int main(){
	struct student stus[10];
	memset(stus, 0, sizeof(stus));
	printf("%d\n", stus[0].ID);
	printf("%s", stus[0].name);
	return 0;
}
相关推荐
XiaoH2338 分钟前
培训机构Day27
java·开发语言·javascript
imning124 分钟前
gateway在eureka注册报java.lang.IndexOutOfBoundsException
java·开发语言
单片机学习之路1 小时前
【STM32】利用SysTick定时器定时1s
c语言·开发语言·stm32·单片机·嵌入式硬件
秋知叶i1 小时前
【轻松学C:编程小白的大冒险】--- 选择 开发工具(IDE)Dev-c++ 03
c语言·开发语言·c++
夏壹-10分分享1 小时前
ThreadLocal为什么会导致内存泄漏?如何解决的?
java·开发语言·jvm
daily_23331 小时前
c++领域展开第十幕——类和对象(内存管理——c/c++内存分布、c++内存管理方式、new/delete与malloc/free区别)超详细!!!!
c语言·c++
连胜优佳1 小时前
19、javase- System类常用方法
java·开发语言
跨海之梦1 小时前
springboot 加载本地jar到maven
开发语言·python·pycharm
weixin_404679311 小时前
Xinference 常见bug: "detail": "Invalid input. Please specify the prompt."
开发语言·python·prompt·bug·pandas
就叫飞六吧1 小时前
C语言中,`extern` 和 `#include`
c语言·c++·算法