C语⾔内存函数

前言

本篇文章将讲解C语⾔内存函数知识的相关内容,为本章节知识的内容。

本篇文章涉及的C语⾔内存函数有:

1.memcpy函数

2.memmove函数

3.memset函数

4.memcmp函数

为本次讲解的知识。

**一、**memcpy函数

1.介绍

void * memcpy ( void * destination, const void * source, size_t num );
memcpy函数strcpy函数功能相似,不同的是memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。

注意:source 和 destination 有任何的重叠,复制的结果都是未定义的,更易懂的角度来说:

例:

int arr[5] = {1, 2, 3, 4, 5};

memcpy(arr+1, arr, 3*sizeof(int)); // 错误:目标区域 (arr+1) 与源区域 (arr) 重叠

memcpy不允许同名数组的复制。

destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。

source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。

num :从source要拷⻉的数据占据的字节数。

返回值:memcpy 函数返回的⽬标空间的起始地址。

使用例:

#include <stdio.h>

#include <string.h>

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

int arr2[10] = { 0 };

memcpy(arr2, arr1, 20);

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr2[i]);

}

return 0;

}

例子讲解:

本代码中,是将 arr1 的前20字节数据复制到 arr2 中,然后打印 arr2 的所有元素。

  • memcpy(arr2, arr1, 20) 表示复制 20字节 数据。由于每个 int 占4字节,因此实际复制 20 / 4 = 5 个元素。
  • arr1 的起始地址复制5个元素:1, 2, 3, 4, 5
  • arr2 初始化为全0,因此未被复制的后5个元素保持为0。

所以可知打印时后5个值为0.

2.模拟实现:

#include <stdio.h>

#include <string.h>

void * mymemcpy ( void * dst, const void * src, int n )

{ void *ret=dst;

while(n--)

{

*(char*)dst=*(char*)src;

dst=(char*)dst+1;

src=(char*)src+1;

}

return ret;

}

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

int arr2[10] = { 0 };

mymemcpy(arr2, arr1, 20);

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr2[i]);

}

return 0;

}

对该实现的讲解:

首先模拟实现要满足原函数的参数,返回值等,所以要满足void * memcpy ( void * destination, const void * source, size_t num );的型状。

因为返回值:memcpy 函数返回的⽬标空间的起始地址。

所以用void *ret=dst;语句来接**⽬标空间的起始地址。**

memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,拷贝的是num个字节,但不同类对应着不同的字节数,所以存在拷贝不完全的情况,而char类型1个字节数,可以完成1个1个复制拷贝的情况,所以 *(char*)dst=*(char*)src; 拷贝完一个字符,就该向下拷贝,但二者均为void *类,所以应该通过强转之后移动地址,所以会有 dst=(char*)dst+1;

src=(char*)src+1; 语句,在结尾,完成返回值:memcpy 函数返回的⽬标空间的起始地址。 return ret;

运行结果一样的,看实现代码,我们可以发现,memcpy不能实现同名数组的复制,如果我们想实现,则可以靠二、memmove函数

二、memmove函数

1.介绍

void * memmove ( void * destination, const void * source, size_t num );
memmove函数也是完成内存块拷⻉的。

他与 memcpy函数功能一样,是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。不过mommove可以正确处理目标空间与源空间重叠的情况。

源内存块和⽬标内存块是可以重叠的。
二者的参数相同,意义也一样的:

destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。

source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。

num :从source要拷⻉的数据占据的字节数。

返回值:memmove 函数返回的⽬标空间的起始地址。

使用例:

#include <stdio.h>

#include <string.h>

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

memmove(arr1 + 2, arr1, 20);

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr1[i]);

}

return 0;

}

讲解:

与在memcpy中的使用方法一样,不同的是该例为同名数组的拷贝,即将该数组的前5个值拷贝从(arr1 + 2)即arr[2]开始。

2.模拟实现:

#include <stdio.h>

#include <string.h>

void * mymemmove ( void * dst, const void * src, int n )

{

void *ret=dst;

if(dst<src)

{

while(n--)

{

*(char*)dst=*(char*)src;

dst=(char*)dst+1;

src=(char*)src+1;

}

}

else

{

while(n--)

*((char*)dst+n)=*((char*)src+n);

}

return ret;

}

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

mymemmove(arr1 + 2, arr1, 20);

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr1[i]);

}

return 0;

}

运行结果一样,接下来对该实现讲解一下:

核心逻辑分析

mymemmove 通过判断目标地址(dst)和源地址(src)的位置关系,选择不同复制方向:

  • dst < src(目标在源左侧):从低地址到高地址复制(避免覆盖未复制的源数据)。(对应图中的一情况)
  • dst >= src(目标在源右侧或重叠) :从高地址到低地址复制(从 n-1 反向遍历)。(对应图中的二情况)

输入参数

  • arr1 初始值:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • mymemmove(arr1 + 2, arr1, 20)
    • dst = arr1 + 2(目标起始地址:第3个元素)
    • src = arr1(源起始地址:第1个元素)
    • n = 20(复制20字节,int 占4字节 → 共复制 5个元素

关键判断

dst (arr1+2) > src (arr1) → 进入 else 分支(从高地址到低地址复制)。

else中拷贝。

while(n--)

*((char*)dst+n)=*((char*)src+n);

最后: return ret;完成返回值: memmove函数返回的⽬标空间的起始地址。

三、memset函数

1.介绍

void * memset ( void * ptr, int value, size_t num );
1.memset 函数是设置内存块的内容的,将内存中指定⻓度的空间设置为特定的内容。 2.memset 的使⽤需要包含<string.h>。
参数讲解:

1.ptr :指针,指向要设置的内存空间,也就是存放了要设置的内存空间的起始地址。

2.value :要设置的值,函数将会把value值转换成unsigned char的数据进⾏设置的。也就是以字节为单位来设置内存块的。

3.num :要设置的内存⻓度,单位是字节。

4.返回值:返回的是要设置的内存空间的起始地址。

使用例:

#include <stdio.h>

#include <string.h>

int main ()

{

char str[] = "hello world";

memset (str,'x',6);

printf(str);

return 0;

}

对于该例的解释:

  1. 初始字符串char str[] = "hello world" 定义了一个字符数组,存储字符串 "hello world"(共11个字符 + 1个结束符 \0,总长度12字节)。

  2. memset 操作

    • 作用:将 str 起始的 6个字节 全部设置为字符 'x'
    • 原字符串前6个字符是 "hello "(注意第6个字符是空格),替换后变为 "xxxxxx"
  3. 剩余字符不变

    • memset 仅修改前6字节,原字符串从第7字节开始的内容("world\0")保持不变。
    • 拼接后结果为 "xxxxxxrld\0"(第7个字符是 'r',因此输出 xxxxxxrld)。

所以是这样的结果。

注意:当有⼀块内存空间需要设置内容的时候,就可以使⽤memset函数,值得注意的是memset函数对内存 单元的设置是以字节为单位的。

2.模拟实现:

#include <stdio.h>

#include <string.h>

void * mymemset ( void * p, int value,int n)

{ void *ret=p;

while(n--)

{

*(char*)p=value;

p=(char*)p+1;

}

return ret;

}

int main ()

{

char str[] = "hello world";

mymemset (str,'x',6);

printf(str);

return 0;

}

讲解:

  • mymemset执行流程
    • 保存原始指针ret = p,用于最终返回。
    • 循环6次(n--从6到0),每次将p指向的字节设为'x',并将p按字节递增((char*)p + 1)。
    • 原字符串前6字节("hello ")被替换为"xxxxxx",后续字符"world\0"保持不变,拼接后结果为"xxxxxxrld\0"
  • printf输出 :打印字符串至结束符\0,结果为 xxxxxxrld

四、memcmp函数

1.介绍

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
功能:

1.⽐较指定的两块内存块的内容,⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节 2.memcmp 的使⽤需要包含
ptr1 :指针,指向⼀块待⽐较的内存块

ptr2 :指针,指向另外⼀块待⽐较的内存块

num :指定的⽐较⻓度,单位是字节

使用例:

#include <stdio.h>

#include <string.h>

int main()

{

char s1[] = "abcdefg";

char s2[] = "abcdEFG";

int n;

n = memcmp(s1, s2, sizeof(s1));

if (n > 0)

printf("'%s'>'%s'.\n", s1, s2);

else if (n < 0)

printf("'%s' <'%s'.\n",s1,s2);

else

printf("'%s' =='%s' .\n", s1, s2);

return 0;

}

讲解:

  • 输出内容'abcdefg' < 'abcdEFG'.
  • 关键原因memcmp字节逐一比较 (区分大小写),在第5个字符处:
    • s1[4] = 'e'(ASCII值101)
    • s2[4] = 'E'(ASCII值69)
      101 > 69memcmp返回正数,n>0, 执行: if (n > 0) printf("'%s'>'%s'.\n", s1, s2);

注意:1.memcmp 函数是通过返回值告知⼤⼩关系的。

2.如果要⽐较2块内存单元的数据的⼤⼩,可以使⽤该函数,这个函数的特点就是可以指定⽐较长度。

2.模拟实现:

#include <stdio.h>

#include <string.h>

int mymemcmp ( const void * p1, const void * p2, int n )

{

const char* s1 = (const char*)p1;

const char* s2 = (const char*)p2;

while (n-- > 0)

{

if (*s1 != *s2)

{

return *s1 - *s2;

}

s1++;

s2++;

}

return 0;

}

int main()

{

char s1[] = "abcdefg";

char s2[] = "abcdEFG";

int n;

n = mymemcmp(s1, s2, sizeof(s1));

if (n > 0)

printf("'%s'>'%s'.\n", s1, s2);

else if (n < 0)

printf("'%s' <'%s'.\n",s1,s2);

else

printf("'%s' =='%s' .\n", s1, s2);

return 0;

}

结果一样,接下来,我将为大家讲解一下:

因为参数为void*类型,如果使用,需要 *(char*)p1 这种的强转,代码中每次比较和移动指针时都进行(char*)强制转换,可通过临时变量优化,const char* s1 = (const char*)p1;

const char* s2 = (const char*)p2;通过char*类型,便于比大小和向下移动。

while (n-- > 0)

{

if (*s1 != *s2)

{

return *s1 - *s2;

}

s1++;

s2++;

}

return 0;

  • 循环条件n-- > 0 表示从第 1 个字节开始,逐个比较 n 个字节(每轮循环后 n 减 1,直到 n=0 退出)。
  • 逻辑
    • 每次循环读取 s1s2 指向的当前字节(*s1/*s2)。
    • 若字节不同,直接返回两字节的 ASCII 码差值(例如 'e' - 'E' = 101 - 69 = 32,结果为正数)。
    • 若字节相同,指针后移(s1++/s2++),继续比较下一个字节。
    • 若循环结束后所有 n 字节均相同(未触发 return *s1 - *s2),则显式返回 0。

总结

以上就是今天要讲的内容,

本篇文章涉及的C语⾔内存函数有:

1.memcpy函数

2.memmove函数

3.memset函数

4.memcmp函数

知识的相关内容,为本章节知识的内容,希望大家能喜欢我的文章,谢谢各位。

相关推荐
程序员卷卷狗3 小时前
JVM 调优实战:从线上问题复盘到精细化内存治理
java·开发语言·jvm
lly2024064 小时前
ASP Folder:深入解析其功能与使用技巧
开发语言
雪域迷影4 小时前
Go语言中通过get请求获取api.open-meteo.com网站的天气数据
开发语言·后端·http·golang·get
deng-c-f6 小时前
配置(4):VScode c/c++编译环境的配置:c_cpp_properties.json
c语言·c++·vscode
ysdysyn6 小时前
C# 进程管理实战:检查与启动EXE程序的完整指南
开发语言·c#
IDOlaoluo6 小时前
PHP-5.2.1.tar.gz 离线安装教程:从源码编译到配置的详细步骤(附安装包)
开发语言·php
wangjialelele7 小时前
Qt中的常用组件:QWidget篇
开发语言·前端·c++·qt
爱上妖精的尾巴8 小时前
5-26 WPS JS宏数组元素添加删除应用
开发语言·前端·javascript·wps·js宏
_OP_CHEN8 小时前
C++进阶:(三)深度解析二叉搜索树原理及实现
开发语言·数据结构·c++·二叉树·二叉搜索树·键值对
wxxka8 小时前
git使用
开发语言·git