在之前学过一些内存函数, 但时间过了久了点就模糊了,甚至将其记忆成了字符串函数,现写篇博客记忆它。
memcpy
函数声明: void * memcpy ( void * destination, const void * source, size_t num );
该函数的作用就是从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
他操作的对象是内存,也就是说它不仅仅可以操作字符串,也可以操作其他类型的数据,接下来看代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int arr1[12] = {1,2,3,4,5,6,7,8,9};
int arr2[2]; // 只分配两个int的空间
int* p = arr1;
int* q = arr2;
// 复制两个int元素(即8个字节,假设int是4字节)
memcpy(q, p, 2 * sizeof(int));
for (int i=0; i<2; i++) // 只遍历arr2的前两个元素
{
printf("%d ", arr2[i]);
}
return 0;
}
这个函数将arr1的前两个元素值复制给了arr2,因而就对arr2进行了赋值,但是这个函数在遇到'\0'并不会停下来,并且这个函数存在一个风险,就是两个指针操作的地址如果存在相互重叠(相同地址)部分,那么函数结果就会是未定义的。
接下来是函数的实现:
void* own_memcpy(void* sou,const void* src,size_t num)
{
void* ret = sou;
while (num--)
{
*(char*)sou = *(char*)src;
sou = (char*)sou + 1;//将内存数据以单个字节逐一传值
src = (char*)src + 1;
}
return ret;
}
因为是操作内存,所以就要函数的逐一为每一个字节传值,而参数num就会是类型大小*元素个数了。在数据类型中只有char类型大小才是1个字节,并且对地址操作就要使用指针类型,那么这样就合理了。
详见:memcpy - C++ 参考 (cplusplus.com)
memmove
函数声明:
void * memmove ( void * destination, const void * source, size_t num );
该函数的作用跟memcpy函数的作用类似,但是此函数可以实现两个内存块上的重叠,因此源空间和目标空间上存在内存重叠,就只能用memmove函数来实现;
。使用示例:
#include <stdio.h>
#include <string.h>
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *src = arr + 2; // 指向 arr[2],即值为3的元素
int *dest = arr; // 指向 arr 的开始位置
// 假设我们想要将 arr[2] 到 arr[4] (包含arr[4])复制到 arr 的开始位置
memmove(dest, src, 3 * sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
上面讲过了memcpy函数,对此这里不必多说,直接实现一下:
void* own_memmove(void*destnation,const void* source,size_t num)
{
void* ret = destnation;
if (destnation<=source)
{
while (num--)
{
(char*)destnation = (char*)source;
destnation=(char*)destnation+1;
source = (char*)source+1;
}
}
else
{
destnation = (char*)destnation + num - 1;//从末尾开始
source = (char*)source + num - 1;
while (num--)
{
*(char*)destnation = *(char*)source;
destnation = (char*)destnation-1;
source = (char*)source-1;
}
}
return ret;
}
函数的复制分为两种情况,主要是针对内存的重叠情况,如果说源地址和目标地址之间存在重叠,并且源地址的起始位置在目标地址之前,那么就应该让函数从两个地址的结束位置开始从后往前赋值,否则采用从前往后赋值就会改变重叠部分,重叠部分也为源地址范围,这样会改变源地址的值
源地址采用const修饰为不可改变,那么编译器就会报错了。那么其余情况如内存间不存在重叠,或者存在重叠,但是源地址在目标地址之后,都可以从两个地址的起始位置从前往后赋值。
详见:memmove - C++ 参考 (cplusplus.com)
memset
函数声明:
void * memset ( void * ptr, int value, size_t num );
memset就是用来设置内存的值的,将内存的值设置为想要的值,
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "this is string!";
memset (str,'-',6);
puts (str);
return 0;
}
在这里采用了int类型的参数值进行传递,但该函数使用此值的无符号字符转换填充内存块。,因此str数组的前6个字符元素变成'-'。
接下来代码的实现:
void* own_memset(void* ptr, int value, size_t num)
{
unsigned char* p = (unsigned char*)ptr; // 转换为无符号字符指针
while (num--)
{
*p = (unsigned char)value;
p++;
}
return ptr;
}
在这里我们将其转换成char/unsigned类型进行赋值(char类型是1个字节)。
memset - C++ 参考 (cplusplus.com)
memcmp
函数声明如下:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
该函数是一个比较函数,目的为比较ptr1和ptr2指针指向位置开始,向后的num个字节,一旦比较出字符的不相等,就会返回值。由于memcmp函数不会改变某一内存的值,为了方便,我直接将函数的实现和memcmp原本的函数放在同一代码好中。
#include<string.h>
#include<stdio.h>
int own_memcmp(const void * ptr1,const void *ptr2,size_t num)
{
while (num--)
{
if (*(char*)ptr1 == *(char*)ptr2)
{
ptr1 = (char*)ptr1 + 1;
ptr2 = (char*)ptr2 + 1;
}
else
return (char*)ptr1 - (char*)ptr2;
}
}
//后者比较前者,如果后者大于前者 返回大于0数,相等返回0,小于0数
int main()
{
char arr[20] = "Hello word ";
char arr2[] = "Helloword";
int a=own_memcmp(arr,arr2,7);//模拟函数在这
printf("%s\n",arr);
printf("%d\n", a);
int b = memcmp(arr, arr2, 7);//这里为库里的memcmp函数使用
printf("%d\n", b );
return 0;
}
如果判断的值有不同的地方,就会直接将两个值相间的值返回,这很合理。
详见:memcmp - C++ 参考 (cplusplus.com)
在以上的强转类型中我都是用的是char/unsigned char,由于C语言引入stdint.h用于uint8_t ,也可以将char/unsigned char改成uint8_t 。
以上就是博客全部内容,欢迎各位的观看、指正。