C语⾔内存函数
前言
本篇文章将讲解C语⾔内存函数知识的相关内容,为本章节知识的内容。
本篇文章涉及的C语⾔内存函数有:
1.memcpy函数
2.memmove函数
3.memset函数
4.memcmp函数
为本次讲解的知识。
一、 memcpy函数
1.介绍
void * memcpy ( void * destination, const void * source, size_t num );
memcpy函数strcpy函数功能相似,不同的是memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。
注意:source 和 destination 有任何的重叠,复制的结果都是未定义的,更易懂的角度来说:
例:
int arr[5] = {1, 2, 3, 4, 5};
memcpy(arr+1, arr, 3*sizeof(int)); // 错误:目标区域 (arr+1) 与源区域 (arr) 重叠
memcpy不允许同名数组的复制。
destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。
source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。
num :从source要拷⻉的数据占据的字节数。
返回值:memcpy 函数返回的⽬标空间的起始地址。
使用例:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
例子讲解:
本代码中,是将 arr1
的前20字节数据复制到 arr2
中,然后打印 arr2
的所有元素。
memcpy(arr2, arr1, 20)
表示复制 20字节 数据。由于每个int
占4字节,因此实际复制20 / 4 = 5
个元素。- 从
arr1
的起始地址复制5个元素:1, 2, 3, 4, 5
。 arr2
初始化为全0,因此未被复制的后5个元素保持为0。
所以可知打印时后5个值为0.
2.模拟实现:
#include <stdio.h>
#include <string.h>
void * mymemcpy ( void * dst, const void * src, int n )
{ void *ret=dst;
while(n--)
{
*(char*)dst=*(char*)src;
dst=(char*)dst+1;
src=(char*)src+1;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
mymemcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
对该实现的讲解:
首先模拟实现要满足原函数的参数,返回值等,所以要满足void * memcpy ( void * destination, const void * source, size_t num );的型状。
因为返回值:memcpy 函数返回的⽬标空间的起始地址。
所以用void *ret=dst;语句来接⽬标空间的起始地址。
memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,拷贝的是num个字节,但不同类对应着不同的字节数,所以存在拷贝不完全的情况,而char类型1个字节数,可以完成1个1个复制拷贝的情况,所以 *(char*)dst=*(char*)src; 拷贝完一个字符,就该向下拷贝,但二者均为void *类,所以应该通过强转之后移动地址,所以会有 dst=(char*)dst+1;
src=(char*)src+1; 语句,在结尾,完成返回值:memcpy 函数返回的⽬标空间的起始地址。 return ret;
运行结果一样的,看实现代码,我们可以发现,memcpy不能实现同名数组的复制,如果我们想实现,则可以靠二、memmove函数。
二、memmove函数
1.介绍
void * memmove ( void * destination, const void * source, size_t num );
memmove函数也是完成内存块拷⻉的。
他与 memcpy函数功能一样,是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。不过mommove可以正确处理目标空间与源空间重叠的情况。
源内存块和⽬标内存块是可以重叠的。
二者的参数相同,意义也一样的:
destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。
source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。
num :从source要拷⻉的数据占据的字节数。
返回值:memmove 函数返回的⽬标空间的起始地址。
使用例:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1 + 2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
讲解:
与在memcpy中的使用方法一样,不同的是该例为同名数组的拷贝,即将该数组的前5个值拷贝从(arr1 + 2)即arr[2]开始。
2.模拟实现:
#include <stdio.h>
#include <string.h>
void * mymemmove ( void * dst, const void * src, int n )
{
void *ret=dst;
if(dst<src)
{
while(n--)
{
*(char*)dst=*(char*)src;
dst=(char*)dst+1;
src=(char*)src+1;
}
}
else
{
while(n--)
*((char*)dst+n)=*((char*)src+n);
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
mymemmove(arr1 + 2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
运行结果一样,接下来对该实现讲解一下:
核心逻辑分析
mymemmove
通过判断目标地址(dst
)和源地址(src
)的位置关系,选择不同复制方向:
- 当
dst < src
(目标在源左侧):从低地址到高地址复制(避免覆盖未复制的源数据)。(对应图中的一情况)- 当
dst >= src
(目标在源右侧或重叠):从高地址到低地址复制(从n-1
反向遍历)。(对应图中的二情况)输入参数
arr1
初始值:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
mymemmove(arr1 + 2, arr1, 20)
:
dst = arr1 + 2
(目标起始地址:第3个元素)src = arr1
(源起始地址:第1个元素)n = 20
(复制20字节,int
占4字节 → 共复制 5个元素)关键判断
dst (arr1+2) > src (arr1)
→ 进入else
分支(从高地址到低地址复制)。在
else中拷贝。
while(n--)
*((char*)dst+n)=*((char*)src+n);最后: return ret;完成返回值: memmove函数返回的⽬标空间的起始地址。
三、memset函数
1.介绍
void * memset ( void * ptr, int value, size_t num );
1.memset 函数是设置内存块的内容的,将内存中指定⻓度的空间设置为特定的内容。 2.memset 的使⽤需要包含<string.h>。
参数讲解:
1.ptr :指针,指向要设置的内存空间,也就是存放了要设置的内存空间的起始地址。
2.value :要设置的值,函数将会把value值转换成unsigned char的数据进⾏设置的。也就是以字节为单位来设置内存块的。
3.num :要设置的内存⻓度,单位是字节。
4.返回值:返回的是要设置的内存空间的起始地址。
使用例:
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "hello world";
memset (str,'x',6);
printf(str);
return 0;
}
对于该例的解释:
初始字符串:
char str[] = "hello world"
定义了一个字符数组,存储字符串"hello world"
(共11个字符 + 1个结束符\0
,总长度12字节)。
memset
操作:
- 作用:将
str
起始的 6个字节 全部设置为字符'x'
。- 原字符串前6个字符是
"hello "
(注意第6个字符是空格),替换后变为"xxxxxx"
。剩余字符不变:
memset
仅修改前6字节,原字符串从第7字节开始的内容("world\0"
)保持不变。- 拼接后结果为
"xxxxxxrld\0"
(第7个字符是'r'
,因此输出xxxxxxrld
)。所以是这样的结果。
注意:当有⼀块内存空间需要设置内容的时候,就可以使⽤memset函数,值得注意的是memset函数对内存 单元的设置是以字节为单位的。
2.模拟实现:
#include <stdio.h>
#include <string.h>
void * mymemset ( void * p, int value,int n)
{ void *ret=p;
while(n--)
{
*(char*)p=value;
p=(char*)p+1;
}
return ret;
}
int main ()
{
char str[] = "hello world";
mymemset (str,'x',6);
printf(str);
return 0;
}
讲解:
mymemset
执行流程:
- 保存原始指针
ret = p
,用于最终返回。- 循环6次(
n--
从6到0),每次将p
指向的字节设为'x'
,并将p
按字节递增((char*)p + 1
)。- 原字符串前6字节(
"hello "
)被替换为"xxxxxx"
,后续字符"world\0"
保持不变,拼接后结果为"xxxxxxrld\0"
。printf
输出:打印字符串至结束符\0
,结果为xxxxxxrld
。
四、memcmp函数
1.介绍
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
功能:
1.⽐较指定的两块内存块的内容,⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节 2.memcmp 的使⽤需要包含
ptr1 :指针,指向⼀块待⽐较的内存块
ptr2 :指针,指向另外⼀块待⽐较的内存块
num :指定的⽐较⻓度,单位是字节
使用例:
#include <stdio.h>
#include <string.h>
int main()
{
char s1[] = "abcdefg";
char s2[] = "abcdEFG";
int n;
n = memcmp(s1, s2, sizeof(s1));
if (n > 0)
printf("'%s'>'%s'.\n", s1, s2);
else if (n < 0)
printf("'%s' <'%s'.\n",s1,s2);
else
printf("'%s' =='%s' .\n", s1, s2);
return 0;
}
讲解:
- 输出内容:
'abcdefg' < 'abcdEFG'.
- 关键原因:
memcmp
按字节逐一比较(区分大小写),在第5个字符处:
s1[4] = 'e'
(ASCII值101)s2[4] = 'E'
(ASCII值69)
因101 > 69
,memcmp
返回正数,n>0, 执行: if (n > 0) printf("'%s'>'%s'.\n", s1, s2);
注意:1.memcmp 函数是通过返回值告知⼤⼩关系的。
2.如果要⽐较2块内存单元的数据的⼤⼩,可以使⽤该函数,这个函数的特点就是可以指定⽐较长度。
2.模拟实现:
#include <stdio.h>
#include <string.h>
int mymemcmp ( const void * p1, const void * p2, int n )
{
const char* s1 = (const char*)p1;
const char* s2 = (const char*)p2;
while (n-- > 0)
{
if (*s1 != *s2)
{
return *s1 - *s2;
}
s1++;
s2++;
}
return 0;
}
int main()
{
char s1[] = "abcdefg";
char s2[] = "abcdEFG";
int n;
n = mymemcmp(s1, s2, sizeof(s1));
if (n > 0)
printf("'%s'>'%s'.\n", s1, s2);
else if (n < 0)
printf("'%s' <'%s'.\n",s1,s2);
else
printf("'%s' =='%s' .\n", s1, s2);
return 0;
}
结果一样,接下来,我将为大家讲解一下:
因为参数为void*类型,如果使用,需要 *(char*)p1 这种的强转,代码中每次比较和移动指针时都进行
(char*)
强制转换,可通过临时变量优化,const char* s1 = (const char*)p1;
const char* s2 = (const char*)p2;通过char*类型,便于比大小和向下移动。while (n-- > 0)
{
if (*s1 != *s2)
{
return *s1 - *s2;
}
s1++;
s2++;
}
return 0;
- 循环条件:
n-- > 0
表示从第 1 个字节开始,逐个比较n
个字节(每轮循环后n
减 1,直到n=0
退出)。- 逻辑:
- 每次循环读取
s1
和s2
指向的当前字节(*s1
/*s2
)。- 若字节不同,直接返回两字节的 ASCII 码差值(例如
'e' - 'E' = 101 - 69 = 32
,结果为正数)。- 若字节相同,指针后移(
s1++
/s2++
),继续比较下一个字节。- 若循环结束后所有
n
字节均相同(未触发return *s1 - *s2
),则显式返回 0。
总结
以上就是今天要讲的内容,
本篇文章涉及的C语⾔内存函数有:
1.memcpy函数
2.memmove函数
3.memset函数
4.memcmp函数
知识的相关内容,为本章节知识的内容,希望大家能喜欢我的文章,谢谢各位。