头文件<string.h>中常用内存操作函数共有四大,学习完本篇文章,各种类型数组的常见处理轻松拿下。
文章目录
零、前言
对字符串(字符数组)的操作函数有很多,但是我们想要操作整型数组等呢:
这就需要内存操作函数了,memory在计算机科学中是内存的意思,这也是四大内存操作函数都有mem头的原因。
与void*类型指针重要的知识:
void*类型指针可以指向任何类型的数据,但是void*类型指针无法访问地址数据。这是因为指针压根就不知道它要访问多大空间,那么即使能访问得到的数据也毫无意义。因此不能对void*类型指针解引用操作,也不能做地址偏移±操作,这是语法型错误。
本文提及的字符串相关函数不熟悉的可以查看文章【C语言】<string.h>中十大字符串函数(用法+模拟实现)。
一、memcpy 内存拷贝函数
我们知道strcpy(字符串拷贝函数)可以将一个字符串内容拷贝到另一个字符串,如果我们想拷贝整型数组,我们可以使用memcpy内存拷贝函数。
memcpy头文件:string.h
memcpy功能:逐字节地拷贝num个字节内存,并会覆盖原来内容。
memcpy函数声明:
void * memcpy ( void * dst, const void * src, size_t num );
src(source)源内存首字节地址,dst(destination)目标内存首字节地址。
num是需要拷贝的字节数。
memcpy返回的是dst首字节地址,并且是void*型,接收返回值需要强制类型转换。
memcpy使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 0 };
int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1, arr2, 4 * sizeof(arr2[0]));
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
printf("\n");
memcpy(arr1+2,arr1,4*sizeof(arr1[0]));
//理想拷贝后效果应该是1 2 1 2 3 4 0 0 0 0
//实际输出是1 2 1 2 1 2 0 0 0 0 因为原来的3 4被提前覆盖了
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
return 0;
}
注意:
dst和src都要有超过num个字节的空间。
源内存块src和目标内存块dst有任何的重叠,复制的结果都是未定义的。
my_memcpy
void *my_memcpy(void *dst, const void *src, int num)
{
//将void*型强制转化成char*,能够逐字节访问
char *tmp_dst = (char *)dst;
const char *tmp_src = (const char *)src;
//循环num次,拷贝num个字节
for (int i = 0; i < num; i++)
{
*tmp_dst++ = *tmp_src++;
}
return dst;
}
二、memmove 内存移动函数
memmove和memcpy的差别就是处理的源内存块和目标内存块是可以重叠的。
memmove头文件:string.h
memmove功能:将源内存块num个字节移动到目标内存块,源内存块内容并不消失。
memmove函数声明:
void * memmove ( void * dst, const void * src, size_t num );
src(source)源内存首字节地址,dst(destination)目标内存首字节地址。
num是需要拷贝的字节数。
memmove返回的是dst首字节地址,并且是void*型,接收返回值需要强制类型转换。
memmove使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 0 };
int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1, arr2, 4 * sizeof(arr2[0]));
printf("arr1:");
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
printf("\narr2:");
for (int i = 0; i < 10; i++)
printf("%d ", arr2[i]);
//理想拷贝后效果应该是1 2 1 2 3 4 0 0 0 0
printf("\narr1自己移动自己:");
memmove(arr1 + 2, arr1, 4 * sizeof(arr1[0]));
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
return 0;
}
注意:dst和src都要有超过num个字节的空间。
my_memmove
memmove的模拟关键在于解决内存块重复部分移动问题。
首先要比较dst和src两个地址大小,原因如下:
- 如果要将1 2 3 4 5 6 7 8的src 1 2 3 4移动到dst 3 4 5 6位置,就要从4开始倒着移动,才能避免重叠部分移动错误。
- 如果要将src 3 4 5 6移动到dst 1 2 3 4位置,就要从3开始正着移动。
- 如果不是重叠内存块拷贝,正着倒着移动都一样,地址位置判断并不影响。
void *my_memmove(void *dst, const void *src, int num)
{
char *tmp_dst = (char *)dst;
const char *tmp_src = (const char *)src;
if (src > dst)
for (int i = 0; i < num; i++)
*tmp_dst++ = *tmp_src++;
else
for (int i = num-1; i >=0; i--)
*(tmp_dst + i) = *(tmp_src + i);
return dst;
}
三、memset 内存赋值函数
memset头文件:string.h
memset功能:给一片内存num个字节赋上指定值。
memset函数声明:
void * memset ( void * ptr, int value, size_t num );
ptr是内存块首字节地址。
value是要赋的值,这个值应该不超过一个无符号字节大小即28-1。
num是需要赋值的字节数。
memset返回值是ptr首字节地址,并且是void*型,接收返回值需要强制类型转换。
memset使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
char carr[10] = { 0 };
memset(carr, 65, sizeof(carr));
printf("字符型:");
for (int i = 0; i < 10; i++)
printf("%c ",carr[i]);
int darr[4] = { 0 };
printf("\n整型:");
memset(darr, 1,sizeof(darr));
for (int i = 0; i < 4; i++)
printf("%d ", darr[i]);
return 0;
}
注意:
内存块要有num个字节大小。
给每个字节赋值,比如int型是4字节,赋值1,输出是16843009而不是1。
my_memset
void *my_memset(void *ptr, int value, int num)
{
//强制类型转换为无符号char*型
unsigned char *tmp_ptr = (unsigned char *)ptr;
//循环num次
for (int i = 0; i < num; i++)
{
//因为char是特殊的整型,所以可以直接赋值
*tmp_ptr++ = value;
}
return ptr;
}
四、memcmp 内存比较函数
memcmp头文件:string.h
memcmp功能:逐字节地比较从ptr1和ptr2开始的num个字节,直至比出。
memcmp函数声明:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
ptr1是一个内存块首字节地址,ptr2是另一个内存块首字节地址。
num是需要比较的字节数。
ptr1>ptr2返回值>0,ptr1<ptr2返回值<0,完全相等返回值0。
memcmp使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 1,2,3,4,5,7,7,7,7,7 };
if (memcmp(arr1, arr2, sizeof(arr1)) > 0)
printf("arr1大");
else if (memcmp(arr1, arr2, sizeof(arr1)) < 0)
printf("arr2大");
else
printf("arr1和arr2一样大");
return 0;
}
注意:内存块要有num个字节大小。
my_memcmp
int my_memcmp(const void *ptr1, const void *ptr2, int num)
{
//先强制类型转换
const char *tmp_ptr1 = (const char *)ptr1;
const char *tmp_ptr2 = (const char *)ptr2;
//循环num次,比较出就返回值
for (int i = 0; i < num; i++)
{
if (*(tmp_ptr1+i) > *(tmp_ptr2+i))
return 1;
else if (*(tmp_ptr1+i) < *(tmp_ptr2+i))
return -1;
}
//比较完num个字节了,仍没返回,完全相等返回值0。
return 0;
}
熟练掌握库函数,敲码速度倍倍翻。
码字不容易,欢迎关注、点赞、收藏、评论、转发。