10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。

简介: 10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。

目录:内存操作函数

一:memcpy函数(内存拷贝)

二:memmove函数(内存拷贝)

三:memset函数(内存设置)

四:memcmp函数(内存比较)


一:memcpy函数

memcpy内存函数的实现原理:                                                                                                    

将 num 个字节的数据从 source 指向的位置开始拷贝到 destination 指向的内存块中,这个函数在遇到0’的时候并不会停下

所以memcpy函数中需要传入三个参数,分别是目标内存空间的首地址,被拷贝内存空间的首地址和拷贝字节数。

memcpy函数可以拷贝字符串数据,整形数组数据,结构体数据等诸多类型......,但因为memcpy内存函数不能够自身拷贝,memcpy函数不能够拷贝内存重叠区的数据,因此存在着一定的局限性,在这里,小夏推荐大家在遇到数据拷贝时应用接下来的memmove函数可以完美的避免所有的不稳定因素。


二:memmove函数

memmove内存函数的实现原理(内存存在重叠,无重叠都可使用):

用于拷贝内存数据,如果目标区域和源区域有重叠的话,

memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中

但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同

memmove函数实现重叠内存过程:

总结来说,在遇到内存重叠的情况下分为三中情况:

1.从前向后拷贝(内存空间存在重叠)->如果source的地址高于disnation的地址:则进行常规的赋值,及从source的地址依次向后对disnation地址进行拷贝,直到拷贝的字节数达到size_t num,及拷贝完成。

2.从后向前拷贝(内存空间存在重叠)->如果source的地址低于disnation的地址:则进行逆序的赋值,及从source+num的地址向前对disnation+num地址进行拷贝,直到拷贝的字节数达到size_t num,及拷贝完成。

3.从前向后拷贝或-从后向前拷贝都可(内存空间不存在重叠)


模拟实现memmove函数:

我们可以自己写一个my_memmove函数来模拟实现memmove函数的功能

首先定义函数void* my_memmove(void* s1, const void* s2, size_t len)

因为memmove是拷贝内存的函数,对于内存中被拷贝的是什么数据类型,并没有限制,所以传入的参数数据类型用void*,返回值也是无类型的地址。

size_len是一个无符号整数类型,表示想要拷贝的字节数,所以在my_memmove函数内部,我们可以这样写

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* s1, const void* s2, size_t len)
{
  assert(s1);
  assert(s2);
  void* ret = s1;
  int i = 0;
  
  if (((char*)s2)>((char*)s1))//从s2的前端依次拷贝
  {
    for(i=0;i<len;i++)
    {
      *((char*)s1) = *((char*)s2);
      (char*)s1 = (char*)s1 + 1;
      (char*)s2 = (char*)s2 + 1;
    }
  }
  else
  {
    while (len--)//从s2的后端开始拷贝
      {
        *((char*)s1 + len) = *((char*)s2 + len);
      }
  }
 
  return ret;
}
int main()
{
  
  int arr[20] = { 0 };
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    arr[i] = i + 1;
  }
  my_memmove(arr + 1, arr , sizeof(int) * 5);
  for (i = 0; arr[i] != 0; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}


三:memset函数

memset内存函数的实现原理:num字节对内存块进行初始化

将value的值依次从ptr地址,依次向后size_t num个字节的内存空间进行赋值


memset的函数定义:

模拟实现memset函数:

我们可以自己写一个my_memset函数来模拟实现memset函数的功能

首先定义函数void* my_memset(void * ptr, int value, size_t num)

因为memset是拷贝内存的函数,对于内存中被拷贝的是什么数据类型,并没有限制,所以传入的参数数据类型用void*,返回值也是无类型的地址。

size_num是一个无符号整数类型,表示想要赋值的字节数,所以在my_memset函数内部,我们可以这样写

#include<stdio.h>
void* my_memset(void* ptr, int value, size_t num)
{
  while (num--)
  {
    *((char*)ptr) = value + '0';
    ptr = (char*)ptr + 1;
  }
}
int main()
{
  char arr[] = "abcdefghijk";
  my_memset(arr+1, 6, 3);
  printf("进过赋值后的结果->%s\n", arr);
  return 0;
}

在模拟实现memset函数中有一个小小的知识点就是:将数字转化成字符该如何转化,字符‘0’的ASCLL值为48,数字0的ASCLL值为0,所以将数字加上字符‘0’就可得到相应的字符

该函数实现的结果为:


四:memcmp函数

memcmp内存函数的实现原理:用于比较比较内存的前N个字节

字符串大小的比较是以ASCII 码表上的顺序来决定,次顺序亦为字符的值。memcmp()首先将s1 第一个字符值减去s2 第一个字符的值,若差为0 则再继续比较下个字符,若差值不为0 则将差值返回。

两个字符串内容完全一样,返回0;

若S1大于S2,则大于0,

反之则小于0;

memcmp的函数定义:从ptr1内存空间和ptr2内存空间依次向后进行比较size_t num个字节大小,且per1和ptr2进const修饰不可改变其内容。


模拟实现memcmp函数:

我们可以自己写一个my_mecmp函数来模拟实现memcmp函数的功能

首先定义函数void* my_memcmp(const void*ptr1,const void*ptr2,size_t num)

#include<stdio.h>
void* my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
  while (num--)
  {
    if (*(char*)ptr1 == *(char*)ptr2)
    {
      ptr1 = (char*)ptr1 + 1;
      ptr2 = (char*)ptr2 + 1;
    }
    else
    {
      return *(char*)ptr1 - *(char*)ptr2;
    }
  }
}
int main()
{
  char s1[] = "abcd456ef";
  char s2[] = "abef789nmn";
  int ret=my_memcmp(s1, s2, 5);
  printf("差值为->%d", ret);
 
  return 0;
}

其执行结果为:

执行结果:

my_memcmp(s1+7, s2,5):1 //字符串s1>字符串s2, 返回正值

my_memcmp(s1, s2,5):-1 // 字符串s1<字符串s2,返回负值

my_memcmp(s1, s2,2):0 //字符串s1=字符串s2, 返回0


如果觉得文章不错,期待你的一键三连哦,你个鼓励是我创作的动力之源,让我们一起加油,顶峰相见!!!

相关文章
|
3天前
|
C语言
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
10 0
|
3天前
|
C语言
【C语言】:4大内存函数
【C语言】:4大内存函数
8 2
|
4天前
|
C语言
C语言内存函数
C语言内存函数
7 0
|
6天前
|
C语言 C++
C语言----C语言内存函数
C语言----C语言内存函数
|
12天前
|
消息中间件 存储 Kafka
实时计算 Flink版产品使用问题之 从Kafka读取数据,并与两个仅在任务启动时读取一次的维度表进行内连接(inner join)时,如果没有匹配到的数据会被直接丢弃还是会被存储在内存中
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
4天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
11 2
|
7天前
|
存储
数据在内存中的存储(2)
数据在内存中的存储(2)
21 5
|
7天前
|
存储 小程序 编译器
数据在内存中的存储(1)
数据在内存中的存储(1)
25 5
|
8天前
|
存储 安全 Java
SpringSecurity6从入门到实战之初始用户如何存储到内存
Spring Security 在 SpringBoot 应用中默认使用 `UserDetailsServiceAutoConfiguration` 类将用户信息存储到内存中。当classpath有`AuthenticationManager`、存在`ObjectPostProcessor`实例且无特定安全bean时,此配置生效。`inMemoryUserDetailsManager()`方法创建内存用户,通过`UserDetails`对象填充`InMemoryUserDetailsManager`的内部map。若要持久化到数据库,需自定义`UserDetailsService`接口实
|
7天前
|
存储 编译器 C语言
数据在内存中的存储
数据在内存中的存储
14 2