C语言之内存函数

简介: C语言之内存函数

在此之间,我们学过许多的字符函数,例如strcpy,strstr,strcmp等等,这里我们以strcpy函数进行举例:

//实现将arr1拷贝到arr2中
int main()
{
  int arr1[5] = { 1,2,3,4,5 };
  int arr2[5] = { 0 };
  strcpy(arr2, arr1);
  return 0;
}

程序并没有正确运行,编译器报错:

错误分析如下:

上述代码是无法实现将arr1拷贝到arr2中,由此可见,像strcmp,strcpy,strstr这种字符串函数,它虽然能够实现字符串拷贝,比较等功能,但是由于它们的操作对象是字符串,因此对于整形数组,浮点型数组等并不适用。


那么对于整形数组,浮点型数组等其他的数据,我们该如何进行操作呢?


c语言给出了另一类函数-------内存函数(memcpy/memmove/memcmp)


memcpy:

//void*-----通用类型指针:可以接受各种类型的参数
void*memcpy(void*destination,const void*source,size_num)//size_num的单位是字节

还是选用上述实例:

#include<stdio.h>
#include<string.h>
//实现将arr1拷贝到arr2中
int main()
{
  int arr1[5] = { 1,2,3,4,5 };
  int arr2[5] = { 0 };
  memcpy(arr2, arr1,sizeof(arr1));
  return 0;
}

此时打开监视窗口,我们不难发现,数组之间完美的实现了拷贝。

结构体类型实现拷贝:

举例:

struct person 
{
  int age;
  char name[20];
  char phone[13];
};
int main()
{
  struct person arr3[] = { {20,"张三","20031319"},{19,"lisa","193684"} };
  struct person arr4[3] = {0};
  memcpy(arr4, arr3, sizeof(arr3));
}

此时打开监视窗口,我们不难发现,结构体之间完美的实现了拷贝。

模拟实现memcpy:

以整形进行举例:

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* scr,size_t num)
{
  void* ret = dest;
  //断言:为避免是空指针
  assert(dest != NULL);
  assert(scr != NULL);
  while (num--)//num为要拷贝的长度
  {
  //不能直接解引用操作的原因:void*类型不能直接进行++/--操作
    *(char*)dest = *(char*)scr;//先强制类型转换在解引用
    dest=(char*)dest+1;
    scr=(char*)scr+1;
  }
  return ret;//不能直接返回dest,因为此时的dest并不是首地址
}
int main()
{
  int arr1[] = { 1,2,3,4,5 };
  int arr2[5] = { 0 };
  my_memcpy(arr2, arr1,sizeof(arr1));
  for (int i = 0; i < 5; i++)
  {
    printf("%d ", arr2[i]);
  }
  return 0;
}
1 2 3 4 5

上述实例,我们是实现的功能是将一个数组中的元素拷贝到另一个数组中,如果现在我们实现将数组中的前几个元素拷贝到后面的几个呢?

举例:

//实现将1 2 3 4 5拷贝到3 4 5 6 7的位置上
#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* scr,size_t num)
{
  void* ret = dest;
  assert(dest != NULL);
  assert(scr != NULL);
  while (num--)
  {
    *(char*)dest = *(char*)scr;
    dest=(char*)dest+1;
    scr=(char*)scr+1;
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memcpy(arr1+2, arr1,20);
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

预期输出结果为:

1 2 1 2 3 4 5 8 9 10

实际输出结果:

1 2 1 2 1 2 1 8 9 10

预期和实际不相符分析如下:

由此可得出:我们所编写的my_memcpy函数并不能实现在同一个数组中的拷贝,会出现数的覆盖现象。

那库函数memcpy能否实现呢?

#include<stdio.h>
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  memcpy(arr1+2, arr1,20);
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

输出结果如下:

1 2 1 2 3 4 5 8 9 10

看到这里,相信很多人都会产生疑惑,为什么库函数memcpy可以实现,而我们编写的my_memcpy不能实现呢?难道是我们写错了?

事实并非如此,memcpy虽然也实现了这样的功能,但C语言标准规定:memcpy:用来处理不重叠的内存拷贝。memmove:处理重叠的内存拷贝


我们所编写的my_memcpy是因为严格按照C语言的标准所编写,而在VS编译器上memcpy超额完成了任务,相当于抢了memmove的饭碗。

下面我们就来学习memmove函数!

memmove:

依然是上述实例:

#include<stdio.h>
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  memmove(arr1+2, arr1,20);
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

输出结果如下:

1 2 1 2 3 4 5 8 9 10

现在得出的结果正是我们预期的效果!

模拟实现memmove:

目的地的地址低于源头地址:

正序进行拷贝:

#include<stdio.h>
#include<assert.h>
my_memmove(void* dest, const void* scr, size_t count)
{
  assert(dest != NULL);
  assert(scr != NULL);
  void* ret = dest;
  while (count--)
  //直接按照源头一个个进行打印,不存在数的覆盖
  {
    *(char*) dest= *(char*) scr;
    dest = (char*)dest + 1;
    scr = (char*)scr + 1;
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr1, arr1+2,20);
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

这种比较简单,这里就不赘述分析过程了

输出结果为:

3 4 5 6 7 6 7 8 9 10

目的地址高于源头地址:

倒序进行拷贝:

#include<stdio.h>
#include<assert.h>
my_memmove(void* dest, const void* scr, size_t count)
{
  assert(dest != NULL);
  assert(scr != NULL);
  void* ret = dest;
  while (count--)
  {
    *((char*)dest + count) = *((char*)scr + count);
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr1+2, arr1,20);
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

输出结果为:

1 2 1 2 3 4 5  8 9 10

对于该行代码的实现过程,我们分析如下:

*((char*)dest + count) = *((char*)scr + count);

对于上述两种情况,我们可用一个程序进行表示:

#include<stdio.h>
#include<assert.h>
my_memmove(void* dest, const void* scr, size_t count)
{
  assert(dest != NULL);
  assert(scr != NULL);
  void* ret = dest;
  if(dest<scr)//正序进行拷贝
  while (count--)
  {
    *(char*)dest = *(char*)scr;
    dest = (char*)dest + 1;
    scr = (char*)scr + 1;
  }
  else//倒序进行拷贝
  {
  while(count--)
  {
    *((char*)dest + count) = *((char*)scr + count);
  }
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr1+2, arr1,20);
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

输出结果如下:

1 2 1 2 3 4 5 8 9 10

memcmp:

将两个存储区的前n个字节进行比较。

举例:

#include<stdio.h>
int main()
{
  int arr[] = { 1,2,3,4,5 };
  int arr2[] = { 1,2,5,4,3 };
  int ret=memcmp(arr, arr2, 9);//arr>arr2,返回大于零的数字,arr<arr2,返回小于零的数字,二者相等,返回0
  //注:VS编译器返回-1/0/1,虽然不严谨,但是并不违背C语言的标准规定
  printf("%d\n", ret);
  return 0;
}

输出结果如下:

-1

分析如下:

memset:内存设置函数

举例:

字符型:

#include<stdio.h>
int main()
{
  char arr[10] = "";
  memset(arr, '#', 10);//10代表更改10个字节
  return 0;
}

整形:

#include<stdio.h>
int main()
{
  int  arr[10] = {0};
  memset(arr, 1, 10);
  return 0;
}

输出如下:

让不少人产生疑惑的是:为什么此时并没有实现将数组中的10个元素都修改为1呢?

原因是:该函数的操作单位是字节,而数组是一个整形数组,其中的元素都为整形,每个元素为4个字节。

具体分析如下:

因此,在使用该函数的时候,一定要注意类型。

目录
打赏
0
0
0
0
5
分享
相关文章
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
39 23
|
3天前
|
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
26 15
|
3天前
|
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
40 24
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
20 1
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
75 10
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
59 9
|
1月前
|
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
55 6
|
1月前
|
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
321 6
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
70 6
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
436 1