玩转内存修改函数——【C语言】

简介: 在上篇博客中,我们学习了字符串函数,针对一些字符串我们可以做出一系列操作。接下来我们将学习一些内存修改函数(#inlcude<string.h>),让我们一起走进mempy、memmove、memcmp函数中。

在上篇博客中,我们学习了字符串函数,针对一些字符串我们可以做出一系列操作。接下来我们将学习一些内存修改函数(#inlcude<string.h>),让我们一起走进mempy、memmove、memcmp函数中。


mempcy函数


5995ce5774e34f34b2bd67a952e76db6.png


通过函数原型,我们可以看出返回值和一些指针参数类型都是void*类型,这就说明此函数不仅仅局限于字符串,针对整型数组、结构体数组等等都可以实现,size_t num参数单位是字节,因为前面指针的类型为void* ,我们并不知道传入数组的元素类型,所以第三个参数的单位为字节就能对很多数据类型进行拷贝。


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


1.参数:destianation指针是接收一个任何类型的数组首元素地址,用作目标。


source指针也是接收同种类型的指针变量,用作拷贝内容。


size_t num是要拷贝的字节数。


2.返回值:返回目标的指针。


3.这个函数在遇到 '\0' 的时候并不会停下来,该函数不检查源中的任何终止空字符 - 它总是准确地复制字节数。


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


接下来对memcpy函数的引用:


int main(void)
{
  int arr1[] = {1,2,3,4,5,6,7,8,9,10};
  int arr2[20] = { 0 };
  memcpy(arr2, arr1, 40);
  int i = 0;
  for (i = 0; i < 20; i++)
  {
    printf("%d ", arr2[i]);
  }
  return 0;
}


将arr1中的40个字节内存内容拷贝到arr2中去。arr2中有20个0,但是只拷贝10个arr1中的元素,所以arr2中有前10个元素将被arr1中的内容覆盖,结果如下:


64c39adfdb6241399f69d5a073b513f0.png


接下来我们进行对memcpy函数的模拟实现:


模拟实现
void* my_memcpy(void* dest, const void* src, size_t num)
{
  void* ret = dest;
  assert(src && dest);
  while (num--)
  {
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }
  return ret;
}
int main(void)
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[20] = { 0 };
  my_mempy(arr2, arr1, 40);
  for (int i = 0; i < 20; i++)
  {
    printf("%d ", arr2[i]);
  }
  return 0;
}


我们创建一个my_memcpy函数,仿照memcpy函数原型来建立自定义函数的原型。因为返回值为目标数组的首元素指针,所以我们先创建一个void*指针对目标数组进行标记。然后使用while循环,进行逐字节赋值。因为memcoy函数可以针对任何种类的数组,所以我们应该将void*类型指针全部强制类型转换为最小单位char*类型的指针,这样就可以兼容所有种类的数组。当赋值完成后进行指针加1操作(对于void*类型的指针,我们不能对其进行++操作,所以我们只能写成dest = (char*)dest + 1;的形式)。当num--到0时,证明已经全部赋值完毕,跳出循环返回最初指针即可。


下面是我们运行的结果:


46a4558a6f3c4d839b21e84166d235de.png


与我们最初使用memcpy函数所得到的结果相同。


接下来我们又有一个问题,我们可以使用memcpy函数将一个数组中的内容,拷贝到自己中吗 ?说更通俗点:有一个整型数组int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };将arr1中的1,2,3,4,5放到3,4,5,6,7的位置上面吗?


int main(void)
{
  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;
}


我们想的到最终arr1中的内容: 1,2,1,2,3,4,5,8,9,10但结果如何呢?


0fdceb535b884cc48dd31727b021ba5b.png

那为什么会是这样呢?


b133f38d6fd04a908ee40634e9717134.png


那我们应该怎么做才能完成刚才的内容呢?这就要使用memmove函数来完成了!!


memmove函数


091a3d78ecc344e1a1edfa0a454e6fcd.png


memmove函数是将移动内存块的函数,将字节数的值从源指向的位置复制到目标指向的内存块。复制就像使用了中间缓冲区一样,允许目标和源重叠。


1.返回值和参数与mempy函数相同,我们可以参照上面进行理解。


2.源指针和目标指针指向的对象的基础类型与此函数无关;结果是数据的二进制副本。

该函数不检查源中的任何终止空字符 - 它总是准确地复制字节数。

为避免溢出,目标参数和源参数指向的数组的大小应至少为字节数。


3.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 如果源空间和目标空间出现重叠,就得使用memmove函数处理。


接下来让我们使用memmove函数完成上面的问题:


int main(void)
{
  int arr1[10] = { 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;
}


aa6347f67efe4ef387291577a88b1f51.png


很丝滑的完成了刚才的问题。


现在让我们对memmove函数进行模拟实现:


void* my_memmove(void* dest,const void* src, size_t num)
{
  void* ret = dest;
  if (dest < src)
  {
    while (num--)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest + 1;
      src = (char*)src + 1;
    }
    return ret;
  }
  else
  {
    while (num--)
    {
      *((char*)dest + num) = *((char*)src + num);
    }
  }
}
int main(void)
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr + 2, arr, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}


创建一个my_memmove函数,仿照memmove原型完成自定义函数的参数。但是我们应该怎么完成函数体呢?


刚才使用最基本的从前向后复制,就得不到想要的结果,如果从后向前复制则可以得到想要数组。有写情况我们又需要从前向后复制,所以我为大家总结一下:


d3445709e054422981ff6071c52f8f88.png


所以我们得分情况来完成自定义函数体,当dest指针<src指针时,里面的内容与my_memcpy内容相同。当dest指针>src指针时,我们继续利用while循环,利用从后向前复制才能完成。 在每次解引用前加上nun字节数即可指针从后往前访问,while判断条件为num--,在判断是否结束时也可以挪动指针,一举两得。最后返回目标数组的首元素地址即可。唯一与my_memcpy函数不同的时必须分类讨论。


运行结果如下:


bab9c60f053141058d9711f44aad9e55.png


此函数模拟完成!


针对以上两个函数,看到这里都会有个疑问,是不是memcpy函数可以干的事memmove函数都可以办到。那肯定的。所以memmove函数的作用肯定是高于mempy函数的!!!


memcmp函数


4c96adeb5253400f8eafeb6f9b67286f.png


memcmp函数是比较两个内存块,这里与strcmp函数有一些相似,都是比较两个数组是否相同,返回值的情况相同。但不同的是memcmp可以比较的类型更多,该函数在找到空字符后不会停止比较,而strcmp会停止。


PTR1


指向内存块的指针。


PTR2


指向内存块的指针。


数字


要比较的字节数。


59646251573a4f5ba21071ab0ec8842a.png


它是通过比较内存中存储的数据进行一个字节一个字节的比较。


举个使用memcmp函数的例子:


int main(void)
{
  int arr1[10] = { 1,2,1,4,5,6 };
  int arr2[10] = { 1,2,257 };
  int ret = memcmp(arr1, arr2, 9);
  printf("%d\n", ret);
  return 0;
}


比较arr1与arr2中前9个字节是否相同?


f5b6d1f0e79e45639e8b39afddc0246a.png


arr1中前10个字节的存储内容


402834bfe34c4eafb846a3eb073afa9d.png


arr2中前10个字节存储内容


因为我们比较的是前9个字节的内容,所以是全部相同的,应该返回输出的值为0; 如果我们比较前10个字节,应该输出的就为-1;


108c79653b4d407ca23b6f47d4dc8a41.png


以上就是我对内存修改函数的全部认识,希望各位大佬在评论区给予我宝贵意见!  

目录
相关文章
|
21天前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
549 0
|
3月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
134 26
|
3月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
250 15
|
9月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
383 23
|
8月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
207 1
一文彻底搞清楚C语言的函数
|
9月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
318 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
|
9月前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
160 24
|
9月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
381 16
|
9月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
210 3

热门文章

最新文章