前言
前面我们学习了字符函数(tolower等)和字符串函数(strcpy、strcmp等),详细内容请转跳至《字符函数和字符串函数!轻松拿下!》,我们可以发现字符串函数虽然好用,但并不通用!使用场景仅限于对字符串的操作,如果我们要拷贝、比较的数据类型是整数或浮点数,字符串函数就哑火了。为此、C语言又为我们提供了不需要考虑数据类型的内存函数来实现这些功能,内存函数通过访问地址的方式操作对象,可以应用在任何类型的对象上面。它们分别是:内存拷贝函数(memcpy和memmove)、内存填充(设置)函数(memset)、内存比较函数(memcmp)。
memcpy ---- 内存拷贝函数
memcpy函数是C和C++标准库中的一个内存拷贝函数,用于将一段内存区域的内容复制到另一段内存区域中。这个函数可以帮助你快速、高效地将数据从一个内存区域复制到另一个内存区域。
其基本用法如下:
void* memcpy( void* destination,const void* source,size_t num);
- memcpy不会管数据类型是什么,它只负责拷贝内存里的数据,因此是void*
- 每个参数对应的意思:destination表示目标地址 source表示源地址 size_t表示拷贝的大小,单位(字节)从源地址拷贝到目标地址。
- 与strcpy遇到\0停止不同,memcpy是不会停止的,而是把\0当成一个普通的数据来对待。
- 假如拷贝重叠部分的数据,一般不会用memcpy,而是由它兄弟memmove去处理。
代码分析:
原理复现:
void* my_memcpy(void* dst, const void* src, size_t count)
{
void* ret = dst;//保存起始地址
assert(dst && src);//断言,防止传入空指针
while (count--) //count为要拷贝的字节数
{
*(char*)dst = *(char*)src;//因为是要一个一个字节去拷贝,因此最好的方式就是使用强制转换成char*
dst = (char*)dst + 1;
src = (char*)src + 1;
}
return ret;//返回起始地址
}
memmove ---- 内存拷贝函数
memove函数与memcpy函数的功能十分相像,memcpy函数在拷贝内容上有个缺陷,就是自己的内容拷贝到自己身上的话会出现错误,而memove比memcpy更加强大,它填补了memcpy的不足,随意可以说memmove是加强版的memcpy函数。
其基本用法如下:
void * memmove ( void * destination, const void * source, size_t num );
关于memcpy自己拷贝自己内容出错原因分析:
假如要把从src(绿色框)位置往后的5个数据拷贝到自身的dest(红色框)位置,我们重点要注意它们的公共部分,使用memcpy拷贝之所以会有错误,是因为memcpy是从前往后拷贝的。当把绿色框的1——>红色框的3位置时,数据3还未被拷贝过就被提前覆盖掉了,此时数组元素为1,2,
1
,4,5,6,7,8,9;再不绿色框的2——>红色框的4位置,变成了1,2,1
,2
,5,6,7,8,9;然后再拷贝,本来应该是把绿色框的3——>红色框的5位置的,但是3被提前覆盖成为了1,所以但再拷贝时。数组变成1,2,1
,2
,1
,6,7,8,9;最终结果为1,2,1
,2
,1
,2
,1
,8,9;而正确的结果应该是1,2,1
,2
,3
,4
,5
,8,9。
此时正确的做法应该是从后往前拷贝,这样就确保了公共部分的数据能被拷贝到
,拷贝完后就可以随便怎么覆盖都行了。
当dest<rsc的时候,我们会发现3->1 4->2 5->3 6->4这样的结果,似乎完全不受顺序影响!也就是说当dest在前面的时候,其实这才是memcpy函数的实现逻辑。
因此,我们才会说memmove是加强版的memcpy函数。
原理复现:
void* my_memmove(void* dest, const void* src, size_t num)
{
char* ret = dest;
if (dest < src)//等同于memcpy的实现逻辑
{
while (num--)
{
*(char*)dest = *(char*)src;//正常的从前往后拷贝
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
else//memove为了填补memcpy的不足的实现逻辑
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);//从后往前拷贝
}
return ret;
}
}
memset ---- 内存填充(设置)函数
memset 是一个 C/C++ 标准库函数,用于将一块内存按指定的值进行填充。
其基本用法如下:
void * memset ( void * ptr, int value, size_t num );
- ptr指向要填充的内存块。
- value 是要填充的值。
- num 是要填充的字节数
- 返回类型是一个指向存储区ptr的指针。
代码分析:
原理复现:
void* my_memset(void* dest, int value, size_t num) {
assert(dest);
void* ret = dest;
while (num--){
*(char*)dest=(char)value;
dest= (char*)dest + 1;
}
}
memcmp ---- 内存比较函数
memcmp函数是在strcmp的基础上,面向所有数据类型比较的内存函数,格式与上面两个内存函数相同,功能也是用于比较大小的。
其基本用法如下:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
看下面是一段比较有趣的代码:
#include <stdio.h>
#include <string.h>
int main()
{
//memcmp,对比内存中数据的大小
int s1[] = { 1,2,3,4,5,6,7,8 };
int s2[] = { 1,2,3,4,6,6,6,6 };
int ret = memcmp(s1, s2, 17);
if (ret > 0)
printf("s1 > s2");
else if (ret < 0)
printf("s1 < s2");
else
printf("s1 == s2");
return 0;
}
代码分析:
注:因为第17个字节拿到了数字6的第一个字节,而vs编译器中是以小端字节序存储的,自然会拿低地址的数据去比对。(至于什么是大小端字节序存储?后面会专门出篇博客详细解释大小端字节序存储的)
memcmp函数本质是上通过每个字节里内容的ascll码值进行大小的比对,如果遇上’\0’,那么ascll码值就是0,除了\0,其他都会得出除了相等以外的结果,这个时候就能比较大小了
原理复现:
int my_memcmp(const void* dest, const void* src, size_t num)
{
assert(dest && src);//断言
while (num--)
{
if (*(char*)dest != *(char*)src)//不相等
{
return *(char*)dest - *(char*)src;//相减得出正负号
}
//相等,则往后比较
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return 0;
}
🎉🎉完结🎊🎊
✨✨最后希望各位小伙伴能多多点赞关注收藏支持鸭!✨✨