【进阶C语言】内存函数(详解)(一)

简介: 【进阶C语言】内存函数(详解)(一)

1. memcpy


1.1 memcpy的介绍

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

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

这个函数在遇到 ‘\0’ 的时候并不会停下来。

如果source和destination有任何的重叠,复制的结果都是未定义的。


1.2 memcpy的使用

用代码举例:

int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[8] = { 0 };
  //把arr1中的前5个数据拷贝到arr2中
  memcpy(arr2, arr1, 20);//strcpy不能用,它只针对字符串拷贝,而上面的是整型数据
  return 0; 
}


试试浮点型看看可不可以:


int main()
{
  float arr1[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
  float arr2[8] = { 0 };
  //把arr1中的前5个数据拷贝到arr2中
  memcpy(arr2, arr1, 12);
  return 0; 
}


从中发现memcpy它并不在乎整型还是浮点型,所以叫他内存拷贝


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

在分析一下上面的信息:

void* – 通用类型的指针,可以接受任意类型数据的地址,但是这种指针不能直接解引用和加减运算!

memcpy函数的设计者,不知道未来程序员使用memcpy拷贝什么类型的数据!

size_t num 表示拷贝多少个字节


1.3 模拟实现memcpy库函数

//memcpy函数返回的是目标空间的起始地址
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
  void* ret = dest;
  assert(dest && src);
  while (num--)
  {
  *(char*)dest = *(char*)src;
  dest = (char*)dest + 1;
  src = (char*)src + 1;
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[8] = { 0 };
  //把arr1中的前5个数据拷贝到arr2中
  my_memcpy(arr2, arr1, 20);
  return 0; 
}


调试监视结果如下:


1.4 我想在1,2后面打印1,2,3,4,5会怎么样?

#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
  void* ret = dest;
  assert(dest && src);
  while (num--)
  {
  *(char*)dest = *(char*)src;
  dest = (char*)dest + 1;
  src = (char*)src + 1;
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  //   你先打印  1 2 1 2 3 4 5 8 9 10
  //   结果却是  1 2 1 2 1 2 1 8 9 10
  my_memcpy(arr1 + 2, arr1, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
  printf("%d ", arr1[i]);
  }
}


红色框框:目标空间

绿色框框:想要拷贝的原数据


结论:

所以我们发现:在内存重叠的时候,使用memcpy可能会出现意想不到的结果

建议在内存重叠的情况,使用memmove函数.


2. memmove


2.1 memmove的介绍

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

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

如果源空间和目标空间出现重叠,就得使用memmove函数处理。


2.2 memmove的使用

int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  //   你先打印  1 2 1 2 3 4 5 8 9 10
  //   结果却是  1 2 1 2 3 4 5 8 9 10
  //说明没有问题
  memmove(arr1 + 2, arr1, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
  printf("%d ", arr1[i]);
  }
}


代码结果:


2.3 模拟实现memmove库函数

红色框框:目标空间

蓝色框框:想要拷贝的原数据

当dest在src前面,也就是dest的地址更低,src的地址更高的时候

整个图片概念图:

从而有两种方案,综合比较,B方案效果更好

B方案图片解释:

代码样子:

if (dest < src)
{
  //前->后
}
else
{
  //后->前
}


完整版代码:

//memcpy函数返回的是目标空间的起始地址
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
  void* ret = dest;
  assert(dest && src);
  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 arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  //             1 2 1 2 3 4 5 8 9 10
  my_memmove(arr1 + 2, arr1, 20);
  //my_memmove(arr1, arr1+2, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
  printf("%d ", arr1[i]);
  }
}


总结:

C语言:memcpy拷贝不重叠的内存

重叠的就交给memmove

memmove > memcpy 100 60

VS:100 100

相关文章
|
1天前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
1天前
|
编译器 程序员 Serverless
|
1天前
|
机器学习/深度学习 C语言
详细解读C语言math.h中常用函数
详细解读C语言math.h中常用函数
|
1天前
|
C语言
C语言刷题(函数)
C语言刷题(函数)
|
19小时前
|
存储 编译器 C语言
C语言的联合体:一种节省内存的数据结构
C语言的联合体:一种节省内存的数据结构
6 0
|
19小时前
|
C语言
C语言中的函数指针、指针函数与函数回调
C语言中的函数指针、指针函数与函数回调
5 0
|
19小时前
|
存储 C语言
C语言中的变量与函数详解
C语言中的变量与函数详解
2 0
|
21小时前
|
存储 C语言
C语言中的printf函数详解
C语言中的printf函数详解
6 0
|
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