一、memcpy使用和模拟实现
函数原型:
void * memcpy ( void * destination, const void * source, size_t num );
dest指向目标内存区域的指针,即数据要复制的地方。sour指向内存区域的指针,即数据要复制的地方。num要复制的字节数。
memcpy函数会将sour所指向的内存区域的前num个字节复制到dest所指向的内存区域。这个函数不会检查目标内存是否有足够的空间来存储复制的数据,也不会检查源内存区域和目标内存区域是否重叠。如果重叠,结果是未定义的,在这种情况下应该使用memmove函数。
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9 };
int arr2[10] = { 0 };
memcpy(arr2,arr1, 5 * sizeof(int)); //这里的num也可以直接填想复制多少字节
for (size_t i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
下面是错误例子也就是上面讲的源内存空间和目标内存空间重叠部分
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9 };
int arr2[10] = { 0 };
memcpy(arr1+3, arr1+4, 5 * sizeof(int)); //在vs编译器中虽然可以编译重叠的部分但是在其他开发环境可能无法实现(重叠不仅仅指完全重叠就算有一个字节重叠也是不行),这个时候我们就要使用memmove来实现重叠部分的拷贝。
for (size_t i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
下面是模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num) {
void* p = dest;
assert(dest && src); //assert是用于避免空指针,如果有空指针就会停止
while (num--) { //num是指一共要交换多少字节。下面的内容是按字节交换
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return p;
}
int main() {
int arr1[10] = { 1,2,3,4,5,6,7,8,9 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1+4, 5 * sizeof(int));
for (size_t i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
二、memmove使用和模拟实现
函数原型:
void * memmove ( void * destination, const void * source, size_t num );
头文件<string.h>
memmove函数从源内存区域src复制n个字节的数据到目标内存区域dest。与memcpy不同的是memmove可以正确的处理源和目标区域重叠的情况。它会将源数据复制到一个临时区域,然后在从临时区域复制到目标区域,以此避免数据覆盖问题。
#include <stdio.h>
#include<string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,0 };
memmove(arr1+2, arr1, 5*sizeof(int)); //memmove函数就可以完美解决源和目标区域重叠的问题(即使在其他开发环境也能实现)
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
我们先在来模拟实现memmove函数
#include <stdio.h>
// 模拟实现memmove函数
void* my_memmove(void* dest, const void* src, size_t n) {
char* ret = (char*)dest;
char* d = (char*)dest;
const char* s = (const char*)src;
// 如果源地址和目标地址相同,直接返回目标地址
if (d == s) {
return dest;
}
// 如果目标地址在源地址之后,为避免数据覆盖,从后往前复制
if (d > s) {
while (n--) {
*(d + n) = *(s + n);
}
}
else {
// 否则,从前往后复制
while (n--) {
*d = *s;
d++;
s++;
}
}
return dest;
}
// 测试代码
int main() {
char str1[14] = "Hello, World!";
char str2[20];
// 测试不重叠的情况
my_memmove(str2, str1, sizeof(str1));
printf("Non-overlapping copy: %s\n", str2);
// 打印重叠复制前的字符串
printf("Before overlapping copy: %s\n", str1);
// 测试重叠的情况
my_memmove(str1, str1 + 2,12); //这里复制 11个和复制12个是完全不一样的复制11个的时候会有一部分未被覆盖,当复制12个的时候虽然也有一部分未被覆盖但是把\0复制了过去,所以没有影响打印出来的结果
// 打印重叠复制后的字符串
printf("Overlapping copy: %s\n", str1);
return 0;
}
下面就是复制了12个的结果
三、memset函数的使用
函数原型:
void * memset ( void * ptr, int value, size_t num );
头文件<string.h>
ptr指向要填充的内存块的指针。可以是数组、结构体等任意类型的内存区域
value是要设置的值。虽然参数类型时int,但实际上只有低8位会被使用,因此可以把它看作是一个unsigned char类型的值。
num是要填充的字节数。这决定了从ptr所指向的地址开始,有多少个字节会被设置位value
#include <stdio.h>
#include <string.h>
int main() {
int arr[10] = { 0 };
memset(arr,1, 4);
printf("%d ",arr[0]);
return 0;
}
肯定有人会认为打印出来的结果是1,其实不然
arr[0]在内存中存放为0x00 00 00 00,
因为num被设置为是4,所以每个字节都被设置为01,,此时它的在内存中存放为0x01 01 01 01。
换算为十进制位结果是16843009
如果想设置为1,则可以将num改为1.
四、memcmp函数的使用
函数原型:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
头文件<string.h>
memcmp函数能将ptr1和ptr2指向的内存块前num个字节进行比较,比较后会返回一个值,具体结果如下
#include <string.h>
#include <stdio.h>
int main() {
int arr1[10] = { 1,2,3,4,5,6,7,8,9,0 };
int arr2[10] = { 1,2,3,4,5,7,8,9,0,6 };
int r = memcmp(arr1, arr2, 6 * sizeof(int));
printf("%d", r);
return 0;
}
arr1和arr2比较前24个字节后返回的结果是-1。