上一篇的字符串函数只是针对字符串的函数,而内存函数是针对内存块的,不在乎内存中存储的数据!这就是字符串函数和内存函数的区别。
准备好爆米花,正片开始
1、memcpy的使用和模拟实现
memcpy库函数的功能:任意类型数组的拷贝
memcpy的函数声明:
void* memcpy(void* destination, const void* source, size_t num);
destination是目标空间,source是源,size_t num是拷贝字节的个数。
为什么还有输入拷贝字节个数呢?
因为memcpy可以拷贝任意类型的数组,可以是字符,可以是int,也可以是struct自定义类型的,但是前提是要输入要拷贝的字节个数,因为传过去的地址被void类型的指针接收,所以不能得知元素大小。
memcpy函数的调用:
#include <stdlib.h>
#include <stdio.h>
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
memcpy函数的模拟实现:
#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* source, size_t num)
{
assert(dest && source);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)source;
dest = (char*)dest + 1;
source = (char*)source + 1;
}
return ret;
}
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
但是这个函数有一个缺点,就是不能重叠内存拷贝,什么意思呢?就是不能在同一个数组中拷贝,会导致打印信息不正确,例如:
#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* source, size_t num)
{
assert(dest && source);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)source;
dest = (char*)dest + 1;
source = (char*)source + 1;
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr+2, arr, 20);更改位置
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
我想将arr+2也就是第3个元素的位置开始,拷贝1-5,我们想象的答案是1 2 1 2 3 4 5 8 9 10,但是实际上的答案是1 2 1 2 1 2 1 8 9 10,因为是从前往后开始拷贝,拷贝信息和拷贝的位置重叠了,导致拷贝时更改了拷贝信息,打印出的结果有所差异。
那怎么办?其实还有memmove函数,他和memcpy的拷贝一样,任意类型都可以拷贝,不同的是memmove可以处理重叠内存拷贝。
2、memmove的使用和模拟实现
memmove库函数功能:拷贝任意类型的数组,也可以处理重叠内存拷贝问题
memmove函数的声明:
void* memmove(void* destination, const void* source, size_t num);
可以看到memmove和memcpy的返回类型和参数一模一样,唯一不同的只是memmove函数的实现细节
memmove函数的调用:
#include <stdlib.h>
#include <stdio.h>
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
memmove究竟是如何处理拷贝重叠的的呢?请继续往下看
memmove函数的模拟实现:
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* source, size_t num)
{
assert(dest && source);
void* ret = dest;
if (dest < source)//如果拷贝的地址小于拷贝信息的地址就可以从前向后拷贝
{
while (num--)
{
*(char*)dest = *(char*)source;//从前向后拷贝
dest = (char*)dest + 1;
source = (char*)source + 1;
}
}
else//如果拷贝的地址大于或等于拷贝信息的地址就从后向前拷贝
{
while (num--)
{
*((char*)dest + num) = *((char*)source + num);//从后向前拷贝
}
}
return ret;
}
int main()
{
int arr[] = { 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 ", arr[i]);
}
return 0;
}
memmove模拟实现逻辑:
我们可以使用地址于地址之间的关系运算,简单概述就是两个地址之间比较大小。因为该函数是排序数组的,数组又是连续存放的,所以可以比较两个地址。如果目标空间地址比源地址大,就从后往前拷贝。如果目标空间地址比源地址小,就可以从前往后拷贝。
3、memset的使用和模拟实现
memory - 记忆(内存),set - 设置。memset就是内存设置的意思。
memset库函数功能:将参数ptr的前num个字节设置成指定的value值。
memset的函数声明:
void* memset(void* ptr, int value, size_t num);
比如我有一个字符数组,字符串是 " hello world " ,我想把它改成 " hello xxxxx",那我们就可以使用memset函数。
memset函数的调用:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[] = "hello world";
memset(str + 6, 'x', 5);
//参数1:字符数组下标6的位置 参数2:需替换的的源值 参数3:字节为单位,向后拷贝的字节大小
printf("%s\n", str);//打印 "hello xxxxx"
return 0;
}
打印结果确实是 " hello xxxxx ",但是我们也可以用它来改变整型数组:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
memset(arr, 1, 20);
int sz = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i=0;i<sz;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
我们想象的是改变前20个字节也就是前5个整型元素,打印为:1,1,1,1,1,6,7,8,9,10,但实际上却却是以每个字节更改为01,并不是我们想象的改为五个1,如下图:
所以你想让它的每个字节都是1是可以做的到的,但是你想让它每个整型都是1这个是做不到的,memset本身就是以字节为单位进行设置的。前面的memcpy和memmove虽然也是以字节为单位来拷贝的,但是它们两边都是在变化着拷贝的,所以能够拷贝正确答案。而这个需拷贝的源始终都是一个值,这个值是不会变化的,每次拷贝一个字节都从这里面的一个字节拷贝到另一个空间。
memset的模拟实现:
#include <stdio.h>
#include <stdlib.h>
void my_memset(void* str, int value, size_t num)
{
assert(str != NULL);
void* ret = str;
while (num--)
{
*(char*)str = (char)value;
str = (char*)str + 1;
}
return ret;
}
int main()
{
char str[] = "hello world";
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
my_memset(str+6, 'x', 5);
my_memset(arr, 1, 20);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
printf("%s\n", str);
return 0;
}
4、memcmp的使用和模拟实现
memcmp库函数的功能:和strncmp的功能一样,strncmp是比较两个字符串的,memcmp是比较两个数组内存的
memcmp函数的声明:
int memcmp(void* ptr1, void* ptr2, size_t num);
memcmp返回值:如果ptr1比ptr2大就返回大于0的数字,如果ptr1比ptr2小就返回小于0的数字,如果相等就返回0
memcmp函数的调用:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int arr1[] = { 1,4,3,4,5 };
int arr2[] = { 1,3,5,7,9 };
int ret = memcmp(arr1, arr2, 5);
printf("%d\n", ret);
return 0;
}
memcmp函数的模拟实现:
#include <stdio.h>
#include <stdlib.h>
int my_memcmp(void* ptr1, void* ptr2, size_t num)
{
assert(ptr1 && ptr2);
while (num--)
{
if (*(char*)ptr1 != *(char*)ptr2)
{
return *(char*)ptr1 - *(char*)ptr2;
}
ptr1 = (char*)ptr1 + 1;
ptr2 = (char*)ptr2 + 1;
}
return 0;
}
int main()
{
int arr1[] = { 1,3,3,4,5 };
int arr2[] = { 1,4,5,7,9 };
int ret = my_memcmp(arr1, arr2, 5);
printf("%d\n", ret);
return 0;
}
end