当前位置:网站首页>【内存操作函数内功修炼】memcpy + memmove + memcmp + memset(四)
【内存操作函数内功修炼】memcpy + memmove + memcmp + memset(四)
2022-08-04 22:42:00 【Albert Edison】
文章目录
1. memcpy - 内存拷贝
前面我们学习了 strcpy 和 strncpy,用于拷贝字符串的库函数,但是,假设我们需要拷贝其他类型的数据呢?比如 浮点型、整型、结构体 ?
这个时候就引出了 memcpy,它不关系是什么类型的数据,只要是存放在内存中的,都可以进行拷贝。
函数介绍
void* memcpy(void* dest, const void* src, size_t num);
memcpy 函数是一个用于拷贝两个不相关的内存块的函数。
memcpy 函数会从 src 的位置开始向后复制 num 个字节的数据到 dest 的内存位置,并返回 dest 的首地址。
注意:
1、memcpy 函数在遇到 '\0'
的时候并不会停下来。
2、如果 src 和 dest 有任何的重叠,复制的结果都是未定义的。
3、memcpy 函数在拷贝的时候,不知道会被用来拷贝什么样的数据类型,所以参数的类型为 void*
(可接收任意类型指针)。
代码实现
假设要把 str1 数组中的前 5 个元素拷贝到 str2 中
#include <stdio.h>
#include <string.h>
int main()
{
int str1[] = {
1,2,3,4,5,6,7,8 };
int str2[5] = {
0 };
memcpy(str2, str1, 5 * sizeof(str1[0]));
for (int i = 0; i < 5; i++) {
printf("%d ", str2[i]);
}
return 0;
}
运行结果
模拟实现
假设要把 str1 数组中的后 5 个元素拷贝到 str2 中
进入 my_memcpy 函数体,首先保存 dest 的起始位置,便于之后返回。
然后循环 num 次,每次将 src 中的一个字节的内存数据拷贝到 dest 中的对应位置;
然后 dest 和 src 指针后移继续拷贝,拷贝结束后返回 dest 原来的首地址即可。
在对 dest 和 src 指针进行操作时,要先将它们强制类型转换为char*
类型的指针。
(char*
类型的指针可以向后访问一个字节的内容)
代码实现
#include <assert.h>
#include <stdio.h>
void* my_memcpy(void* dest, const void* src, size_t num) {
assert(dest && src);
void* start = dest;
while (num--) {
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return start;
}
int main()
{
int str1[] = {
1,2,3,4,5,6,7,8,9,10 };
int str2[5] = {
0 };
my_memcpy(str2, str1+5, 5 * sizeof(str1[0]));
for (int i = 0; i < 5; i++) {
printf("%d ", str2[i]);
}
return 0;
}
运行结果
2. memmove - 内存拷贝
memmove 和 memcpy 的作用是一模一样的,但是唯一的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的。
而 memcpy 函数的源内存块和目标内存块是不可以重叠的。
函数介绍
void* memmove(void* dest, const void* src, size_t num);
注意:
1、memmove 函数处理的源内存块和目标内存块是可以重叠的。
2、如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。
代码实现
假设我们要把 str1 数组中的 1、2、3、4、5,拷贝放到 3、4、5、6、7 的位置
#include <stdio.h>
#include <string.h>
int main()
{
int str1[] = {
1,2,3,4,5,6,7,8,9,10 };
memmove(str1 + 2, str1, 20);
for (int i = 0; i < 10; i++) {
printf("%d ", str1[i]);
}
return 0;
}
运行结果
模拟实现
假设我们要把 str1 数组中的 1、2、3、4、5,拷贝放到 3、4、5、6、7 的位置
那么这个怎么模拟实现呢?其实很简单,往下看
这种情况是 倒着拷,但是还有一种,假设我们要把 str1 数组中的 3、4、5、6、7 拷贝放到 1、2、3、4、5 的位置
那么这个怎么模拟实现呢?其实很简单,往下看
所以我们要分类讨论,通过画图可以得知,可以分为三类情况;
第一类:当 dest 指针位于 src 指针内存块左边,采用 从前向后 拷贝。
第二类:当 dest 指针位于 src 指针内存块里面,采用 从后向前 拷贝。
第三类:当 dest 指针位于 src 指针内存块右边,采用 从前向后 和 从后向前 都可以。
**注:**当 dest 指针与 src 指针位于同一位置时不用拷贝。
代码实现
#include <assert.h>
#include <stdio.h>
void* my_memmove(void* dest, const void* src, size_t num) {
assert(dest && src);
void* start = dest;
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 start; //返回dest的起始地址
}
int main()
{
int str1[] = {
1,2,3,4,5,6,7,8,9,10 };
my_memmove(str1 + 2, str1, 20);
for (int i = 0; i < 10; i++) {
printf("%d ", str1[i]);
}
return 0;
}
运行结果
3. memcmp - 内存比较
函数介绍
int memcmp(const void* buf1, const void* buf2, size_t count);
memcmp 函数是一个用于比较两个内存块大小的函数。
它会比较从 buf1 和 buf2 指针开始的 count 个字节;
当 buf1 大于 buf2 的时候返回一个 大于0 的数;
当 buf1 等于 buf2 的时候返回 0;
当 buf1 小于 buf2 的时候返回一个 小于0 的数。
代码实现
#include<stdio.h>
#include<string.h>
int main()
{
int str1[] = {
1,2,3,4 };
int str2[] = {
1,2,4,5 };
int ret1 = memcmp(str1, str2, 8);
int ret2 = memcmp(str1, str2, 9);
printf("%d\n", ret1);
printf("%d\n", ret2);
return 0;
}
运行结果
在 VS2019 编译器下,内存采用的是 小端字节序 存储方式,str1 和 str2 在内存中的存储形式如图所示
所以,当比较字前 8 个字节数时,str1 等于 str2,所以返回值为 0;
当比较字前 9 个字节数时,前 8 个字节是相等的,第 9 个字节是 str1 小于 str2,所以返回值为 小于0;
4. memset - 内存设置
函数介绍
void *memset( void *dest, int c, size_t count );
memset 函数可以将内存块的某一部分设置为特定的字符。
第一个参数 dest 是开始设置内存的起始位置;
第二个参数 c 是要将内存设置成的字符;
第三个参数 count 是从起始位置开始需要设置的内存的字节数。
注意:memset 函数设置内存的时候是一个字节一个字节地设置的。
代码实现
假设我们要把 arr 数组中的前 10 个字节数设置为 0
#include<stdio.h>
#include<string.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
memset(arr, 0, 10);
for (int i = 0; i < 10; ++i) {
printf("%d ", arr[i]);
}
return 0;
}
运行结果
在 VS2019 编译器下,内存采用的是 小端字节序 存储方式,arr1 在内存中的存储形式如图所示
这个和 memcmp 是比较有点类似,就是要逐个字节进行比较!
5. 总结
以上就是我们 C 语言中常用的内存操作函数,其实理解起来不难,重要是多加使用练习
边栏推荐
猜你喜欢
随机推荐
PAN3020 Sub-1G无线收发芯片
各行各业都受到重创,游戏行业却如火如荼,如何加入游戏模型师职业
ES 数据聚合、数据同步、集群
Both synchronized and ReentrantLock are smooth, because they are reentrant locks, and a thread will not deadlock if it takes the lock multiple times. We need reentrant locks
ANT1.7 download and configuration method
Ts——项目实战应用enum枚举
三个多月、40余场面试浓缩为6000字
【3D建模制作技巧分享】ZBrush模型如何添加不同材质
【论文笔记KDD2021】MixGCF: An Improved Training Method for Graph Neural Network-based Recommender Systems
3D建模师为了让甲方爸爸过稿,还可以这么做,就是在赚血汗钱啊
Latex快速插入作者ORCID
SQL Server 调用 WebService
【3D建模制作技巧分享】ZBrush模型制作流程:地精
The Record of Reminding myself
postman接口测试
【Social Marketing】WhatsApp Business API: Everything You Need to Know
2022年全网最全接口自动化测试框架搭建,没有之一
湖仓一体电商项目(五):内网穿透工具-网云穿
QT[一] 信号与槽
【3D建模制作技巧分享】zbrush贴图映射小技巧