C语言基础入门—内存函数
详细讲解了内存函数memcpy,memmove,memset以及memcmp的相关知识。
个人相关信息:
😊 笔者主页:ristarry
📖 个人专栏:C语言基础入门
✨ 合抱之木,生于毫末,九层之台,起于累土
在字符串函数中所有函数的作用对象都只能是字符串,但是实际上还有这样一类函数,它们直接作用于内存块,因此它们的作用对象无关类型,无关内容,这就是我们今天要讲解的内存函数。
所有内存函数的使用都必须包含头文件: string.h
1. memcpy
void * memcpy ( void * destination, const void * source, size_t num );
功能:
将源字符串source中的num个字节的数据,拷贝到目标字符串destination当中。
注意:
目标字符串和源字符串尽量不要重叠,会引发错误。
(比如说目标字符串是 str1 + 1,源字符串是 str1)
当目标字符串和源字符串重叠时使用memmove函数。
参数:
destination : 目标字符串,是数据拷贝后存放的地方。
source : 源字符串,拷贝的数据就源自这里。
num : 拷贝数据的字节数。
返回值:
目标字符串destination的起始地址。
代码演示:
#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, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++) {
printf("%d ", arr2[i]); //输出1 2 3 4 5 0 0 0 0 0
}
return 0;
}
模拟实现:
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dst, const void* src, size_t count)
{
void* ret = dst;//用于最后返回地址
assert(src);
assert(dst); //断言,防止dst和src为空指针
while (count--) //根据拷贝个数设置终止条件
{
*(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 };
my_memcpy(arr2, arr1, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++) {
printf("%d ", arr2[i]);
}
return 0;
}
在这段代码中有一个很重要的想法,通过将dst和src转换为char*类型,这时进行+1的操作就只会改变一个字节,正好对应上了count的类型。
2. memmove
void * memmove ( void * destination, const void * source, size_t num );
功能:
主要功能和memcpy函数极为相似,都是拷贝num个字节的源字符串到目标字符串中,区别在于memmove函数能够处理源字符串和目标字符串重叠的情况。
参数:
destination : 目标字符串,是数据拷贝后存放的地方。
source : 源字符串,拷贝的数据就源自这里。
num : 拷贝数据的字节数。
返回值:
目标字符串destination的起始地址。
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1 + 2, arr1, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++) {
printf("%d ", arr1[i]);//输出结果为:1 2 1 2 3 4 5 8 9 10
}
return 0;
}
我们借助画图的形式来了解重叠时究竟是怎么拷贝的:

我们看到这里有一个数组arr1,红色部分是目标字符串dst,蓝色部分是源字符串src,每个字符串都截取了20个字节大小的空间。
我们现在来想,要将src拷贝到dst中去,如果是顺序的话:
- 将1拷贝给3 变成 1 2 1 4 5 6 7 8 9 10
- 将2拷贝给4 变成 1 2 1 2 5 6 7 8 9 10
- 将1拷贝给5 变成 1 2 1 4 1 6 7 8 9 10
我们发现不妙:第一次拷贝时,1将3给覆盖了,导致在3要传递时错误地将1给传递过去了。
顺序行不通我们就倒着来呗:
- 将5拷贝给7 变成 1 2 3 4 5 6 5 8 9 10
- 将4拷贝给6 变成 1 2 3 4 5 4 5 8 9 10
- 将3拷贝给5 变成 1 2 3 4 3 4 5 8 9 10
- 将2拷贝给4 变成 1 2 3 2 3 4 5 8 9 10
- 将1拷贝给3 变成 1 2 1 2 3 4 5 8 9 10
这样就正确了,因此我们知道memmove能够处理这种字符串重叠情况的主要原因就是:它能根据自己的需要选择顺序拷贝,还是逆序拷贝。
具体是怎么实现的,我们在下面模拟实现时进行讲解。
模拟实现:
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memmove(void* dst, const void* src, size_t count)
{
assert(dst);
assert(src);//断言
void* ret = dst;//返回地址
//dst在src之前,或者完全不重叠,顺序拷贝
if (dst < src || (char*)dst >= (char*)src + count)
{
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else //dst在src之后,逆序拷贝;
{
while (count--)
{
*((char*)dst + count) = *((char*)src + count);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1 + 2, arr1, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++) {
printf("%d ", arr1[i]);//输出结果为:1 2 1 2 3 4 5 8 9 10
}
return 0;
}
实际上我们经过简单的分类讨论就能发现:
刚刚我们说当dst在src之后时,我们需要逆序拷贝。

现在我们再来看看dst在src之前的情况,这时逆序就行不通了,必须通过顺序拷贝才行。

因此分类讨论的依据就是dst和src的相对位置:
- dst在src之前,顺序拷贝。
- dst和src完全不重叠,怎么拷贝都行,为简便计算,归于顺序拷贝。
- dst和src完全重叠,不需要考虑,直接返回原来的地址。
- dst在src之后,逆序拷贝。
将上述讨论实现到代码上也就是一个if语句就解决了。
3. memset
void * memset ( void * ptr, int value, size_t num );
功能:
将指定字符串ptr的num个字节都设置为一个指定的字符value。
参数:
ptr : 要设置的指定字符串。
value : 要设置的字符,例如 ' x '。
num : 拷贝数据的字节数。
返回值:
指定字符串ptr的地址。
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "abcdefgh";
memset(str, 'x', 5 * sizeof(char));
printf("%s\n", str);//输出:xxxxxfgh
return 0;
}
4.memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
功能:
按字节逐个比较两块区域的内容,直到找到第一个不同的字节,或者将设置的字节数num找完。
参数:
ptr1 和 ptr2 待比较的两块区域的指针。
返回值:
| 返回值 | 含义 |
|---|---|
| < 0 | ptr1 中第一个不同字节的数值 < ptr2 中对应字节的数值(按 ASCII / 二进制值比较) |
| = 0 | 前 num 个字节的内容完全相同 |
| > 0 | ptr1 中第一个不同字节的数值 > ptr2 中对应字节的数值 |
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6 };
int arr2[] = { 1,2,3,5,6,7 };
int r = memcpy(arr1, arr2, sizeof(arr1));//这里sizeof算的是整个数组元素的字节数
if (r > 0)
printf("arr1 > arr2\n");
else if (r == 0)
printf("arr1 == arr2\n");
else
printf("arr1 < arr2\n"); //输出:arr1 > arr2
return 0;
}

更多推荐

所有评论(0)