深度解析C标准库:从函数调用到自主实现

测试环境基于VS2022版本

一、strcpy 

        1.函数介绍:

strcpy用于将源字符数组中的内容(包括终止符 \0)​按字节顺序复制到目标字符数组中

函数原型:

char *strcpy( char *strDestination, const char *strSource );
  • strDestination        指向目标数组的指针,用于存放复制后的字符串
  • strSource               指向要复制的源字符串的指针(不可修改)
  • 返回值:目标数组起始地址(即strDestination的值)

        2.函数的使用
strcpy
的使用需要引入头文件<string.h>
接下来分别创建两个数组,将一个数组中的内容拷贝到另一个数组中去

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[20] = "Hello";
	char arr2[20] = "Good night";
	printf("拷贝前:%s\n", arr1);
	strcpy(arr1, arr2);
	printf("拷贝后:%s\n", arr1);
	return 0;
}

执行代码后,输出内容如下: 

拷贝前:Hello
拷贝后:Good night

可以发现,strcpy复制到目标数组是直接覆盖,那么接下来我们模拟实现以下库函数strcpy

3.strcpy的模拟实现 

函数原型:

char *strcpy( char *strDestination, const char *strSource );
 

观察函数原型 ,函数的两个参数是目标空间与源空间,又通过之前的介绍可知,返回类型为目标空间的起始地址。

要实现字符的复制,可以通过找到两块空间的地址,然后解引用,把需要拷贝的字符放入目标空间中,接着两块指针地址+1,重复上述步骤,当遇到字符串的结束标识后便停止复制

有了以上内容之后,想要模拟实现就异常简单了。

首先定义函数:

char* my_strcpy(char* dest,const char* sorce);

接下来实现字符的拷贝操作:

*dest = *sorce;
dest++;
sorce++;

 遇到标识符就停止复制,使用while循环,遍历数组,当遇到'\0\时候就跳出循环

 最后,返回目标空间的起始位置,那么在函数开头部分定义一个变量存储该位置,最后返回该变量的值就可以解决。
这样,strcpy函数我们就可以模拟出来了:

char* my_strcpy(char* dest, const char* sorce)
{
	char* ret = dest;
	while (*sorce != '\0')
	{
		*dest = *sorce;
		dest++;
		sorce++;
	}
	return ret;
}

函数验证:

int main()
{
	char arr1[20] = "Hello";
	char arr2[20] = "Good Night";
	printf("复制前:arr1 = %s\n", arr1);
	char* p1 = my_strcpy(arr1, arr2);
	printf("复制后:arr1 = %s\n", arr1);
	printf("返回p1 = %s\n", p1);
	return 0;
}

执行代码后,输出内容如下:

复制前:arr1 = Hello
复制后:arr1 = Good Night
返回p1 = Good Night 

结果与strcpy函数的输出内容一致,成功实现strcpy的模拟。

strcpy与strnpy核心原理一致,区别在于strcpy是全部内容均复制,strnpy则是代表复制n个字符

二、strnpy

    1.函数介绍:

strncpy用于将源字符数组中的内容(包括终止符 \0)​按字节顺序复制到目标字符数组中

函数原型:

char * strncpy ( char * destination, const char * source, size_t num );
  • strDestination        指向目标数组的指针,用于存放复制后的字符串
  • strSource               指向要复制的源字符串的指针(不可修改)
  • num                        需要复制的元素个数
  • 返回值:目标数组起始地址(即strDestination的值)

        2.函数的使用
strncpy
的使用需要引入头文件<string.h>
接下来分别创建两个数组,将一个数组中的内容拷贝到另一个数组中去

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[20] = "abcdefghi";
	char arr2[20] = "1234567";
	printf("拷贝前:%s\n", arr1);
	strncpy(arr1, arr2,5);
	printf("拷贝后:%s\n", arr1);
	return 0;
}

执行代码后,输出内容如下: 

拷贝前:abcdefghi
拷贝后:12345fghi

3.strncpy的模拟实现 

函数原型:

char * strncpy ( char * destination, const char * source, size_t num );

观察函数原型 ,函数的两个参数是目标空间与源空间,又通过之前的介绍可知,返回类型为目标空间的起始地址。

要实现字符的复制,可以通过找到两块空间的地址,然后解引用,把需要拷贝的字符放入目标空间中,接着两块指针地址+1,重复上述步骤,当遇到字符串的结束标识后便停止复制

有了以上内容之后,想要模拟实现就异常简单了。

首先定义函数:

char * my_strncpy ( char * destination, const char * source, size_t num );

接下来实现字符的拷贝操作:

*dest = *sorce;
dest++;
sorce++;

使用循环结构拷贝字符,每处理一个字符就将计数器num减1,直至num归零时退出循环。最终返回数组的首地址。

char* my_strncpy(char* dest, const char* sorce,size_t num)
{
	char* ret = dest;
	while (num--)
	{
		*dest = *sorce;
		dest++;
		sorce++;
	}
	return ret;
}

函数验证:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
char* my_strncpy(char* dest, const char* sorce,size_t num)
{
	char* ret = dest;
	while (num--)
	{
		*dest = *sorce;
		dest++;
		sorce++;
	}
	return ret;
}
int main()
{
	char arr1[20] = "abcdefghi";
	char arr2[20] = "1234567";
	printf("拷贝前:%s\n", arr1);
	my_strncpy(arr1, arr2,5);
	printf("拷贝后:%s\n", arr1);
	return 0;
}

执行代码后,输出内容如下:

拷贝前:abcdefghi
拷贝后:12345fghi

结果与strncpy函数的输出内容一致

三、strcmp

        1.函数介绍:

strcmp是用于两个字符串的对比

函数原型:

int strcmp( const char *string1, const char *string2 );
  • string1        指向待比较的第一个字符串(以空字符终止的字符数组)
  • string2        指向待比较的第二个字符串(以空字符终止的字符数组)    
  • 比较规则:
    • 逐字符比较两个字符串对应位置上的字符(按ASCII码值),直至遇到不相等的字符或字符串结束符'\0'。
  • 返回值
    • 若string1 > string2 返回1
    • 若string1 < string2 返回-1
    • 若string1 = string2 返回0

        2.函数的使用
strcmp
的使用需要引入头文件<string.h>
接下来分别创建两个字符数组,用于字符的比较。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void strcmp_ret(int ret,char* str1,char* str2)
{
	if (ret > 0)
		printf("%s > %s\n",str1,str2);
	else if (ret == 0)
		printf("%s == %s\n",str1,str2);
	else
		printf("%s < %s\n",str1,str2);
}
int main()
{
	char arr1_1[10] = "abcde";
	char arr1_2[10] = "abcd";

	char arr2_1[10] = "abcde";
	char arr2_2[10] = "abcdef";

	char arr3_1[10] = "abcde";
	char arr3_2[10] = "abcde";

	char arr4_1[10] = "abcde";
	char arr4_2[10] = "abcdddddd";


	int arr1 = strcmp(arr1_1, arr1_2);
	int arr2 = strcmp(arr2_1, arr2_2);
	int arr3 = strcmp(arr3_1, arr3_2);
	int arr4 = strcmp(arr4_1, arr4_2);
	
	strcmp_ret(arr1, arr1_1, arr1_2);
	strcmp_ret(arr2, arr2_1, arr2_2);
	strcmp_ret(arr3, arr3_1, arr3_2);
	strcmp_ret(arr4, arr4_1, arr4_2);

	return 0;
}

执行代码后,输出内容如下: 

  "abcde" > "abcd"     
  "abcde" < "abcdef"    
  "abcde" == "abcde"    
  "abcde" > "abcdddddd" 


结果剖析:

1. "abcde" > "abcd"     → 第五位 'e'> 空字符
2. "abcde" < "abcdef"    → 第六位 空字符 < 'f'
3. "abcde" == "abcde"    → 全等匹配直至终止符
4. "abcde" > "abcdddddd" → 第五位 'e' > 'd'

可见,strcmp的比较机制为:从首字符开始逐个比较ASCII值
1. 当遇到两个字符串的首个不匹配字符时,返回基于该字符比较的结果
2. 若扫描中某个字符串遇到终止符'\0'而另一个未结束,则认定前者较小
3. 所有字符匹配且结束条件同步时返回0

3.strcmp的模拟实现 

函数原型:

int strcmp( const char *string1, const char *string2 );

观察函数原型 ,函数的两个参数是需要进行对比的两个字符数组。

前面已经提到,strcmp的比较机制,那么想要模拟实现就尤其简单了

首先定义函数:

int my_strcmp(const char* str1, const char* str2);

从两个字符串的起始位置开始同步扫描
每次提取单个字符进行ASCII值比较
关键循环逻辑

while (*str1 != '\0' && *str2 != '\0' (*str1 == *str2))
{
	str1++;
	str2++;
}

当且仅当遇到以下情况 则循环结束:
   1. str1和str2当前字符不相等 
   2. str1或str2遇到'\0' 

最后,比较当前str1与str2的元素
这样,strcmp函数我们就可以模拟出来了

int my_strcmp(const char* str1, const char* str2)
{
	while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
	{
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else if (*str1 < *str2)
		return -1;
	return 0;

}

函数验证:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void strcmp_ret(int ret,char* str1,char* str2)
{
	if (ret > 0)
		printf("%s > %s\n",str1,str2);
	else if (ret == 0)
		printf("%s == %s\n",str1,str2);
	else
		printf("%s < %s\n",str1,str2);
}
int my_strcmp(const char* str1, const char* str2)
{
	while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
	{
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else if (*str1 < *str2)
		return -1;
	return 0;

}
int main()
{
	char arr1_1[10] = "abcde";
	char arr1_2[10] = "abcd";

	char arr2_1[10] = "abcde";
	char arr2_2[10] = "abcdef";

	char arr3_1[10] = "abcde";
	char arr3_2[10] = "abcde";

	char arr4_1[10] = "abcde";
	char arr4_2[10] = "abcdddddd";


	int arr1 = my_strcmp(arr1_1, arr1_2);
	int arr2 = my_strcmp(arr2_1, arr2_2);
	int arr3 = my_strcmp(arr3_1, arr3_2);
	int arr4 = my_strcmp(arr4_1, arr4_2);
	
	strcmp_ret(arr1, arr1_1, arr1_2);
	strcmp_ret(arr2, arr2_1, arr2_2);
	strcmp_ret(arr3, arr3_1, arr3_2);
	strcmp_ret(arr4, arr4_1, arr4_2);

	return 0;
}

执行代码后,输出内容如下:

abcde > abcd
abcde < abcdef
abcde == abcde
abcde > abcdddddd

结果与strcmp函数的输出内容一致,成功实现strcmp的模拟。

四、strncmp

        1.函数介绍:

strncmp是用于两个字符串的对比(指定比对个数)

函数原型:

int strncmp ( const char * str1, const char * str2, size_t num );
  • string1        指向待比较的第一个字符串(以空字符终止的字符数组)
  • string2        指向待比较的第二个字符串(以空字符终止的字符数组)    
  • num            要比较的最大元素个数。
  • 比较规则:
    • 逐字符比较两个字符串对应位置上的字符(按ASCII码值),直至遇到不相等的字符或字符串结束符'\0',或者达到比对元素个数。
  • 返回值
    • 若string1 > string2 返回  1
    • 若string1 < string2 返回 -1
    • 若string1 = string2 返回  0

        2.函数的使用
strncmp
的使用需要引入头文件<string.h>
接下来分别创建两个字符数组,用于字符的比较。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
	char str1[10] = "abcde";
	char str2[10] = "abcd";

	int ret = strncmp(str1, str2,4);

	if (ret > 0)
		printf("%s > %s\n", str1, str2);
	else if (ret == 0)
		printf("%s == %s\n", str1, str2);
	else
		printf("%s < %s\n", str1, str2);
	return 0;
}

执行代码后,输出内容如下: 

abcde == abcd


结果剖析:

abcde == abcd       -->设置的最大带比较元素个数为4,最多比对到第4个结束对比

可见,strcnmp的比较机制为与strncmp一致,只是strncmp添加了一个最大比对元素个数的限制条件。

3.strncmp的模拟实现 

函数原型:

int strncmp(const char* str1, const char* str2, size_t num);

观察函数原型时,该函数的两个参数是需要比较的两个字符数组。

前文已介绍过strncmp的比较机制,因此模拟实现起来就变得非常简单。

首先定义函数:

int my_strncmp(const char* str1, const char* str2, size_t num);

从两个字符串的起始位置开始同步扫描
每次提取单个字符进行ASCII值比较

if (*str1 > *str2)
		return 1;
else if (*str1 < *str2)
		return -1;

比较后若未返回结果,说明当前两元素相等,此时将str1和str2的指针各后移一位。但移动前需检查指针是否到达数组末尾,避免越界访问。若任一指针已到末尾,则不再移动,表明该数组的所有元素已比对完毕。在未达到最大比较次数num的情况下,另一数组必然更大。

if (*str1 != '\0' && *str2 != '\0' )
{
	str1++;
	str2++;
}

最后,当比较次数达到最大值num时退出循环。若此时仍未发现str1和str2的不同元素,则返回0,表示这两个字符数组的前num个元素完全相同。

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	while (num--)
	{
		if (*str1 > *str2)
		{
			return 1;
		}
		else if (*str1 < *str2)
		{
			return -1;
		}
		if (*str1 != '\0' && *str2 != '\0' )
		{
			str1++;
			str2++;
		}
	}
	return 0;
}

函数验证:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
	while (num--)
	{
		if (*str1 > *str2)
		{
			return 1;
		}
		else if (*str1 < *str2)
		{
			return -1;
		}
		if (*str1 != '\0' && *str2 != '\0' )
		{
			str1++;
			str2++;
		}
	}
	return 0;
}
void strncmp_ret(int ret, char* str1, char* str2)
{
	if (ret > 0)
		printf("%s > %s\n", str1, str2);
	else if (ret == 0)
		printf("%s == %s\n", str1, str2);
	else
		printf("%s < %s\n", str1, str2);
}
int main()
{
	char str1[10] = "abcde";
	char str2[10] = "abcd";

	int ret1 = my_strncmp(str1, str2, 4);
	int ret2 = my_strncmp(str1, str2, 7);

	strncmp_ret(ret1, str1, str2);
	strncmp_ret(ret2, str1, str2);
	return 0;
}

执行代码后,输出内容如下:

abcde == abcd
abcde > abc

结果与strncmp函数的输出内容一致,成功实现strncmp的模拟。

五、strlen

        1.函数介绍:

strlen通常用于计算字符串的长度

函数原型:

size_t strlen ( const char * str );
  • str        指向需计算长度的字符串    
  • 计算规则:
    • 逐字符计算字符串字符个数,直至遇到字符串结束符'\0'。
  • 返回值
    • 字符数组中元素个数

        2.函数的使用
strlen
的使用需要引入头文件<string.h>
接下来创建一个字符数组,尝试求出他的长度。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main()
{
	char str1[20] = "abcdef";
	char str2[20] = "abc def";
	
	size_t ret1 = strlen(str1);
	size_t ret2 = strlen(str2);

	printf("ret1 = %zd\n", ret1);
	printf("ret2 = %zd\n", ret2);

	return 0;
}

执行代码后,输出内容如下: 

ret1 = 6
ret2 = 7


结果剖析:

1. ret1 = 6    → 第6位 之后为 终止符
2. ret2 = 7    → 第7位 之后为终止符

可见,strlen的计算机制为:从首字符开始遇到一个字符,计数器就+1,直到遇见终止符'\0'

3.strlen的模拟实现 

函数原型:

size_t strlen ( const char * str );

观察函数原型 ,函数的参数为待计算长度的字符串  

前面已经提到,strlen的计算机制,那么想要模拟实现就尤其简单了

首先定义函数:

size_t my_strlen ( const char * str );

核心逻辑为从首元素开始遍历数组,直到遇到终止符才停下

while (*str != '\0')
{
	str++;
}

那么再定义一个计数器,当指针向后移动一位,计数器就+1;

size_t count = 0;
while (*str != '\0')
{
	count++;
	str++;
}

最后,返回计数器的值

size_t my_strlen(const char* str)
{
	size_t count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

函数验证:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
size_t my_strlen(const char* str)
{
	size_t count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char str1[20] = "abcdef";
	char str2[20] = "abc def";
	
	size_t ret1 = my_strlen(str1);
	size_t ret2 = my_strlen(str2);

	printf("ret1 = %zd\n", ret1);
	printf("ret2 = %zd\n", ret2);

	return 0;
}

执行代码后,输出内容如下:

ret1 = 6
ret2 = 7

结果与strlen函数的输出内容一致,成功实现strlen的模拟。

 六、strstr

        1.函数介绍:

strstr 用于字符串的子串查找

函数原型:

char* strstr(const char* str1, const char* str2)
  • str1        指向被查找的母串    
  • str2         指向需要查找的内容(子串)
  • 查找规则:
    • 逐字符对比,遇到结束标识符或出现不同字符时退出比对。
  • 返回值
    • 若找到,返回子串首元素在母串中出现的位置
    • 若未找到,返回NULL

        2.函数的使用
strstr
的使用需要引入头文件<string.h>
接下来分别创建一个母串和子串

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main()
{
	char strA_1[30] = "This is a simple string";
	char strA_2[30] = "simple";

	char strB_1[30] = "abccba";
	char strB_2[30] = "cba";

	char strC_1[30] = "This is a simple string";
	char strC_2[30] = "NO";

	char* A = strstr(strA_1, strA_2);
	char* B = strstr(strB_1, strB_2);
	char* C = strstr(strC_1, strC_2);

	printf("A = %s\n",A);
	printf("B = %s\n",B);
	printf("C = %s\n",C);
	
	printf("&strA_1 = %p\n", strA_1);
	printf("     &A = %p\n", A);

	printf("&strB_1 = %p\n", strB_1);
	printf("     &B = %p\n", B);

	printf("&strC_1 = %p\n", strC_1);
	printf("     &C = %p\n", C);

	return 0;
}

执行代码后,输出内容如下: 

A = simple string
B = cba
C = (null)
&strA_1 = 0000003882EFFB48
     &A = 0000003882EFFB52
&strB_1 = 0000003882EFFBC8
     &B = 0000003882EFFBCB
&strC_1 = 0000003882EFFC48
     &C = 0000000000000000


结果剖析:

  1. 案例A:

    • 输出字符串: "simple string" (表示在母串中找到子串,并返回从子串首次出现位置开始的字符串)
    • 母串首地址: 0000003882EFFB48
    • 子串首地址: 0000003882EFFB52 (母串中找到子串的起始位置)
  2. 案例B:

    • 输出字符串: "cba" (找到子串)
    • 母串首地址: 0000003882EFFBC8
    • 子串首地址: 0000003882EFFBCB (母串中找到子串的起始位置)
  3. 案例C:

    • 输出: (null) (表示未找到子串)
    • 母串首地址: 0000003882EFFC48
    • 返回地址: 0000000000000000 (空指针,表示未找到)

3.strlen的模拟实现 

函数原型:

char* my_strstr(const char* str1, const char* str2)

观察函数原型 ,函数的参数为被查询的字符串和要查询的字符串

首先定义函数:

char* my_strstr(const char* str1, const char* str2)

核心逻辑为从首元素开始遍历数组,直到遇到不相同的元素或终止符才停下

while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
{
	str1++;
	str2++;
}

需要注意的是,上述代码存在一个问题:当需要查找母串中间的元素时,可能无法进入循环。解决方法是将母串当前元素与子串首元素进行对比,若不相同,则将指针移向母串的下一个元素,继续与子串首元素进行比对,直到找到匹配项或遇到结束符为止。

while (*str1 != '\0' && *str2 != '\0' && *str1 != *str2)
{
	str1++;
}
while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
{
	str1++;
	str2++;
}

当母串为"abcabbb"、子串为"abb"时,按照上述方法进行匹配(如加粗部分所示),可能会出现误判母串中不存在子串的情况。如何解决这个问题?

解决方案如下:

  1. 创建一个临时指针tmp,初始指向母串的首字符
  2. 采用前述匹配方法进行比对:
    • 若首次匹配未找到子串,则tmp指针后移一位
    • 重复上述过程,直至出现以下情况之一: a) tmp指向终止符'\0',说明母串中确实不存在子串,返回NULL b) 子串指针到达终止符前所有字符均匹配成功,说明找到子串,返回当前tmp指针

如图所示,直到s2遇到结束标志或者tmp遇到结束标志,就返回值

  • s2遇到结束标志代表母串中包含子串,返回子串首次出现位置开始的字符串
  • tmp遇到结束标识代表直到最后一刻都没有找到子串, 返回NULL
char* my_strstr(const char* str1, const char* str2)
{
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* tmp = str1;
	while (*tmp)
	{
		s1 = tmp;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return tmp;
		}
		tmp++;
	}
	return NULL;
}

函数验证:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
char* my_strstr(const char* str1, const char* str2)
{
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* tmp = str1;
	while (*tmp)
	{
		s1 = tmp;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return tmp;
		}
		tmp++;
	}
	return NULL;
}

int main()
{
	char strA_1[30] = "This is a simple string";
	char strA_2[30] = "simple";

	char strB_1[30] = "abccba";
	char strB_2[30] = "cba";

	char strC_1[30] = "This is a simple string";
	char strC_2[30] = "NO";

	char* A = my_strstr(strA_1, strA_2);
	char* B = my_strstr(strB_1, strB_2);
	char* C = my_strstr(strC_1, strC_2);

	printf("A = %s\n",A);
	printf("B = %s\n",B);
	printf("C = %s\n",C);
	
	printf("&strA_1 = %p\n", strA_1);
	printf("     &A = %p\n", A);

	printf("&strB_1 = %p\n", strB_1);
	printf("     &B = %p\n", B);

	printf("&strC_1 = %p\n", strC_1);
	printf("     &C = %p\n", C);

	return 0;
}

执行代码后,输出内容如下:

A = simple string
B = cba
C = (null)
&strA_1 = 00000090035BF578
     &A = 00000090035BF582
&strB_1 = 00000090035BF5F8
     &B = 00000090035BF5FB
&strC_1 = 00000090035BF678
     &C = 0000000000000000

结果与strstr函数的输出内容一致,成功实现strstr的模拟。

七、memcpy

        1.函数介绍:

memcpy是完成内存块的拷贝,不关心具体内容是什么

函数原型:

void * memcpy ( void * destination, const void * source, size_t num );
  • destination                     指向目标空间,拷贝后的内容存放在该空间中             
  • source                     指向源空间,需要拷贝的内容从该空间中拿取
  • num                    拷贝的数据大小(以字节为单位)(如4个int类型,就需填值为20)
  • 复制规则:
    • 逐字节拷贝内容,直至达到设置的字节数。

        2.函数的使用
memcpy
的使用需要引入头文件<string.h>
接下来创建两个数组,尝试将其中一个数组的内容拷贝到另一个数组中去。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main()
{
	int arr1[10] = { 1,2,3,4,5 };
	int arr2[10] = { 5,6,7,8,9 };
	memcpy(arr1, arr2, sizeof(int) * 4);
	int i = 0;
	for ( i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

执行代码后,输出内容如下: 

5 6 7 8 5


结果剖析:

5 6 7 8 5  填入拷贝字节数为20,在arr2中为前4个元素,将前4个元素拷贝(覆盖)到arr1中

3.memcpy的模拟实现 

函数原型:

void * memcpy ( void * destination, const void * source, size_t num );

函数原型的几个参数前面已经介绍,这里不再赘述

首先定义函数:

void * my_memcpy ( void * destination, const void * source, size_t num );

核心逻辑为从首元素开始以字节为单位进行拷贝,直到达到设置的拷贝数据大小。

while (num--)
{
	*(char*)destination = *(char*)source;
	(char*)destination += 1;
	(char*)source += 1;
}

最后,以防目标空间与源空间指针为NULL,添加告警(assert)(需引入头文件<assert.h>)

void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination);
	assert(source);
	while (num--)
	{
		*(char*)destination = *(char*)source;
		(char*)destination += 1;
		(char*)source += 1;
	}
}

函数验证:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination);
	assert(source);
	while (num--)
	{
		*(char*)destination = *(char*)source;
		(char*)destination += 1;
		(char*)source += 1;
	}
}
int main()
{
	int arr1[10] = { 1,2,3,4,5 };
	int arr2[10] = { 5,6,7,8,9 };
	my_memcpy(arr1, arr2, sizeof(int) * 4);
	int i = 0;
	for ( i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

执行代码后,输出内容如下:

5 6 7 8 5

结果一致

八、memcmp

       1.函数介绍:

memcmp的左右是对比str1与str2指向的内容

函数原型:

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  • ptr1                     指向待比较的第一块内存空间             
  • ptr2                     指向待比较的另一块内存空间
  • num                    需要比较的数据大小(以字节为单位)(如4个int类型,就需填值为20)
  • 比对规则:
    • 逐字节比对内容,直到出现不同或达到设置的字节数。
  • 返回值:
    • 若ptr1 > ptr2 返回  1
    • 若ptr1 < ptr2 返回 -1
    • 若ptr1 = ptr2 返回  0

        2.函数的使用
memcpy
的使用需要引入头文件<string.h>
接下来创建两个数组,尝试比较两数组的大小关系。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
	char str1[20] = "S&hd8*/+=ds";
	char str2[20] = "S&hd8*/+=ds";

	int ret = memcmp(str1, str2, sizeof(char) * 11);
	if (ret > 0)
	{
		printf("%s > %s\n", str1, str2);
	}
	else if (ret == 0)
	{
		printf("%s == %s\n", str1, str2);
	}
	else
	{
		printf("%s < %s\n", str1, str2);
	}
	return 0;
}

执行代码后,输出内容如下: 

S&hd8*/+=ds == S&hd8*/+=ds


结果剖析:

S&hd8*/+=ds == S&hd8*/+=ds  逐字节对比,对比11个字节后没有找到不同,则输出==

3.memcpy的模拟实现 

函数原型:

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

函数原型的几个参数前面已经介绍,这里不再赘述

首先定义函数:

void * my_memcpy ( void * destination, const void * source, size_t num );

从两个字符串的起始位置开始同步扫描
每次提取单个字符进行ASCII值比较

由于函数参数是void*类型,在使用前需要先进行逐字节的类型转换。void*指针虽然可以指向任何数据类型,但由于它本身没有类型信息,编译器无法知道它指向的具体数据类型,因此无法直接进行指针运算解引用操作。

考虑到memcpy是通过逐字节操作实现的,将参数转换为char*类型是最合适的选择。

if (*(char*)ptr1 > *(char*)ptr2)
{
	return 1;
}
else if(*(char*)ptr1 < *(char*)ptr2)
{
	return -1;
}

比较后若未返回结果,说明当前两元素相等,此时将str1和str2指针各后移一位。但移动前需检查指针是否到达数组末尾,避免越界访问。若任一指针已到末尾,则不再移动,表明该数组的所有元素已比对完毕。在未达到最大比较次数num的情况下,另一数组必然更大。

	if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
	{
		(char*)ptr1 += 1;
		(char*)ptr2 += 1;
	}

然后,函数参数中设置的最大比较数是num个字节,所以再将上述代码放到一个循环中,让其最多执行num次

	while (num--)
	{
		if (*(char*)ptr1 > *(char*)ptr2)
		{
			return 1;
		}
		else if(*(char*)ptr1 < *(char*)ptr2)
		{
			return -1;
		}
		if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
		{
			(char*)ptr1 += 1;
			(char*)ptr2 += 1;
		}
	}

 最后,以防ptr1与ptr2指向的空间为空(NULL),添加告警(assert)(需引入头文件<assert.h>)以避免程序崩溃。

#include <assert.h>
assert(ptr1);
assert(ptr2);

 模拟memcpy函数:

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1);
	assert(ptr2);
	while (num--)
	{
		if (*(char*)ptr1 > *(char*)ptr2)
		{
			return 1;
		}
		else if(*(char*)ptr1 < *(char*)ptr2)
		{
			return -1;
		}
		if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
		{
			(char*)ptr1 += 1;
			(char*)ptr2 += 1;
		}
	}
return 0;
}

函数验证:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1);
	assert(ptr2);
	while (num--)
	{
		if (*(char*)ptr1 > *(char*)ptr2)
		{
			return 1;
		}
		else if(*(char*)ptr1 < *(char*)ptr2)
		{
			return -1;
		}
		if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
		{
			(char*)ptr1 += 1;
			(char*)ptr2 += 1;
		}
	}
	return 0;
}
int main()
{
	char str1[20] = "S&hd8*/+=ds";
	char str2[20] = "S&hd8*/+=ds";

	int ret = memcmp(str1, str2, sizeof(char) * 11);
	if (ret > 0)
	{
		printf("%s > %s\n", str1, str2);
	}
	else if (ret == 0)
	{
		printf("%s == %s\n", str1, str2);
	}
	else
	{
		printf("%s < %s\n", str1, str2);
	}
	return 0;
}

执行代码后,输出内容如下:

S&hd8*/+=ds == S&hd8*/+=ds

结果一致


end

测试环境基于VS2022版本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值