个人相关信息:

😊 笔者主页: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. 将1拷贝给3          变成  1  2  1  4  5  6  7  8  9  10
  2. 将2拷贝给4          变成  1  2  1  2  5  6  7  8  9  10
  3. 将1拷贝给5          变成  1  2  1  4  1  6  7  8  9  10

我们发现不妙:第一次拷贝时,1将3给覆盖了,导致在3要传递时错误地将1给传递过去了。

顺序行不通我们就倒着来呗:

  1. 将5拷贝给7          变成  1  2  3  4  5  6  5  8  9  10
  2. 将4拷贝给6          变成  1  2  3  4  5  4  5  8  9  10
  3. 将3拷贝给5          变成  1  2  3  4  3  4  5  8  9  10
  4. 将2拷贝给4          变成  1  2  3  2  3  4  5  8  9  10
  5. 将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的相对位置:

  1. dst在src之前,顺序拷贝。
  2. dst和src完全不重叠,怎么拷贝都行,为简便计算,归于顺序拷贝。
  3. dst和src完全重叠,不需要考虑,直接返回原来的地址。
  4. 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;
}

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐