c语言的内存函数
C 语言标准库的内存操作函数定义在 <string.h> 头文件中,直接按字节操作内存块,不依赖字符串结束符\0,可处理任意类型数据。以下是 4 个核心函数:
1. memcpy — 内存拷贝
void *memcpy(void *dest, const void *src, size_t n);
功能:从src内存地址拷贝n个字节到dest地址
返回值:目标内存首地址dest
注意:不处理内存重叠,若dest与src区域重叠,结果未定义;重叠场景必须用memmove
以下函数中,将arr中的前20个字节的元素拷贝到arr2中去,也就是只拷贝5个整形1,2,3,4,5
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}

(1)memcpy的模拟实现
void my_memcpy(void* dest, const void* src, size_t num)
{
char* ret = dest;
assert(dest && src);
while(num--)
{
*(char*)dest = *(char*) src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int src[] = { 1,2,3,4,5,6,7,8,9,10 };
int dest[10] = { 0 };
my_memcpy(dest, src, 40);
for (int i = 0; i < 10; i++)
{
printf("%d ", dest[i]);
}
return 0;
}
2. memmove — 可重叠内存拷贝
void *memmove(void *dest, const void *src, size_t n);
功能:与memcpy完全一致,支持内存重叠区域的安全拷贝
原理:内部判断重叠方向,自动选择从前往后或从后往前拷贝,保证数据正确
场景:数组内元素移位、同一块内存的前后拷贝
以下代码中,属于重叠内存的拷贝,将arr数组中的前20个字节,也就是前5个整形拷贝到arr+2往后的地址中去,arr+2指向元素3的位置,拷贝的后的arr数组就变为1,2,1,2,3,4,5,8,9,10
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 2, arr, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}

(1)memmove的模拟实现
在模拟实现memmove函数时我们需要注意,因为拷贝的是在同一个内存空间里拷贝的,所以我们得分情况讨论
第一种,当dest指针在src后面时:
我们将src中的元素赋值给dest的位置时,可以看出,先把4赋给6,然后5赋给7,但是这时注意,这里的6和7已经变成了4和5,所以继续往后赋值,原本是将6赋值给8的,但是现在6的位置已经变成了4了,所以只能将4赋值给8,然后再啊5赋给9,最后再把4赋值给10
这样就达不到我们想要的效果,既然正这赋值不行,那我们可以反着赋值,先将8赋给10,然后将7赋给9,依次赋值,这样就避免了上述重复赋值情况
第二种,dest在src前面时:
这时我们就可以正着赋值了,先将5赋给2,然后依次类推,6赋给3……
第三种,dest在src后面且不重合
这时候,无论我们正着赋值,还是倒着赋值都可以
我们将上述三种情况总结,当dest在src前面时,我们选择正赋值,当dest在src后面时,我们选择倒赋值
正赋值和上面模拟实现memcpy函数一样
while(num--)
{
*(char*)dest = *(char*) src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
主要是怎么实现倒赋值呢,我们首先要让指针指向dest和src的最后一位,怎么做呢,我们知道数组名也表示首元素地址,那么总数组名+数组长度-1就指向了数组最后一位元素,我们就可以写如下代码
*((char*)dest + num) = *((char*)src + num);
来分别找到dest和src的最后一位,然后依次赋值即可,完整代码实现如下
void my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);
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 ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 2, arr, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
3. memset — 内存填充
void *memset(void *ptr, int value, size_t n);
功能:将ptr指向的前n个字节全部设置为value(按字节赋值)
注意:按字节赋值,只能准确初始化0或-1(全 0 / 全 1 字节模式),不能直接给整型数组赋值其他数值
int main()
{
char s[] = "hello world";
memset(s, 'i', 5);
printf("%s ", s);//s=iiiii world
return 0;
}
为什么不能给整形数组赋值呢,观察以下代码
int main()
{
int arr[] = { 1,2,3,4,5 };
memset(arr, 2, 4);
for (int i = 0; i < 5; i++)
printf("%d ", arr[i]);
return 0;
}
我们将2赋给arr数组的第一个元素,刚好是4个字节,但是我们将其打印出来观察结果
发现第一个元素不是2,为什么呢?这是因为memset函数是一个一个字节赋值的,下arr数组的内存
可以发现,它的确将第一个元素的4个字节全部赋值成了2,但是数字2是由00 00 00 02构成的,而02 02 02 02合在一起并不是数字2
4. memcmp — 内存比较
int memcmp(const void *ptr1, const void *ptr2, size_t n);
功能:按字节比较两个内存块的前n个字节
返回值:
< 0:ptr1 小于 ptr2
= 0:两块内存完全相等
> 0:ptr1 大于 ptr2
注意:遇到\0不会停止比较,这是与strcmp的核心区别
int main()
{
char s1[] = "abcde";
char s2[] = "abcdf";
int ret=memcmp(s1, s2, 5);
if (ret < 0)
{
printf("s1<s2");
}
else if (ret == 0)
{
printf("s1==s2");
}
else
{
printf("s1>s2");
}
return 0;
}

更多推荐


所有评论(0)