模拟实现qsort、strlen、strcpy、strcat、strcmp、strstr、memcpy、memmove函数

本文详细介绍了C语言中快速排序函数`qsort`的使用及模拟实现,以及字符串操作函数`strlen`、`strcmp`、`strcpy`、`strcat`的原理与应用。同时,探讨了内存操作函数`memcpy`和`memmove`的功能及其实现。通过实例展示了这些函数如何在实际编程中发挥作用,帮助读者深入理解C语言的基础知识。
摘要由CSDN通过智能技术生成

目录

一、快速排序 qsort

1.介绍使用:

 2:模拟实现:

二、字符串操作函数

1.strlen

(1)介绍使用

(2)模拟实现

2.strcmp

(1)介绍使用

(2)模拟实现

3.strcpy

(1)介绍使用

(2)模拟实现

4.strcat

(1)介绍使用

(2)模拟实现

5.strstr

三、内存操作函数

1.memcpy

(1)介绍使用

(2)模拟实现

2.memmove

(1)介绍使用

(2)模拟实现


一、快速排序 qsort

1.介绍使用:

我们先来看看这个函数的类型及使用方法:

该函数需要的头文件是<stdlib.h>或<search.h>,返回的类型是void;

简单介绍一下他的参数:

           第一个:void *base     是一个voidl型的指针,所以我们在这里可以放任何类型的参数(int、char、float等),base 是首元素的地址;

        第二个:size_t num     是要排序元素的个个数;

        第三个:size_t width      是以字节为单位的数组元素大小;

        第四个:int (__cdecl *compare )(const void *elem1, const void *elem2 )     是比较两个元素大小返回的数值:前一个元素大于后一个元素返回1,等于则返回0,小于则返回-1(在不同编译器下返回值可能不一样,这里是VS编译器),如下:

        

 举个例子吧,我们用整型数组举例:

int com_par(const void* e1, const void* e2)//void*指针可以接受任何类型的数据
{
	return *(int*)e1 - *(int*)e2;
}//比较两个整型大小,得出一个返回值

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

//如果用浮点型数组,比较大小这一段代码部分只能如下:不然返回的类型为int则会将1.1-1.0得出值为0
int com_par2(const void* e1, const void* e2)//void*指针可以接受任何类型的数据
{
	if (*(float*)e1 - *(float*)e2>0)
	{
		return 1;
	}
	if (*(float*)e1 - *(float*)e2 < 0)
		return -1;
	if (*(float*)e1 - *(float*)e2 == 0)
		return 0;
}

 这里我们得出的是升序排序,如果要得到降序排序可以将比较大小函数那里返回值:return *(int*)e1 - *(int*)e2改为return *(int*)e2-*(int*)e1,就可以实现降序排序,结果如下:

 2:模拟实现:

由上已经介绍过该函数的各个参数,所以就先直接上代码:

void my_qsort(void* base, int num, int width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0, j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

不知道你是否察觉到我们的代码形式上是不是像冒泡排序的代码,没错,这就是根据冒泡排序的类型来实现的。

这里的cmp((char*)base + j * width, (char*)base + (j + 1) * width)中的width是指一个元素的字节大小,所以这里的j*width与(j+)*width所表示的就是前一个元素与后一个元素,就这样比较挨着的两个元素,判断返回值r如果>0(前者大于后者,升序排序,如果要降序排序就改为<0)就交换这

两个元素, 这里我们就编写一个Swap函数来进行交换:

void Swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

 然后在主函数调用:

int main()
{
	int arr[10] = { 1,3,2,5,4,6,8,7,9,0 };
	my_qsort(arr, 10, sizeof(arr[0]), cmp_int);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
}
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

得出结果为:

二、字符串操作函数

头文件为<string>

1.strlen

(1)介绍使用

strlen,返回的是一个数值,使用时的参数为char*类型的数组名(即字符串首的地址);使用如下:

#include<string.h>
#include <stdio.h>
int main()
{
	char arr[20] = "abcdefghijk";
	int len = strlen(arr);
	printf("len=%d\n", len);
	return 0;
}

 注意:只能实现字符串计数。

(2)模拟实现

先上代码:

int my_strlen(const char* arr)
{
	int n = 0;
	while (*arr++ != '\0')
	{
		n++;//只要arr不等于‘\0’,循环一次n就加1
	}
	return n;
}

只要从首元素开始接着往后走n+1,当*arr为‘\0’时,即arr这个数组中的元素个数统计完得到n,最后返回数值n;运行如下:

2.strcmp

(1)介绍使用

比较两个字符的大小,用ASCALL码比较。

它的两个参数都是char*类型,返回的类型为int型,如下:

举个例子来说明吧:

int main()
{
	char arr1[10] = "c";
	char arr2[10] = "h";
	int et = strcmp(arr1, arr2);
	printf(" %d", et);
	return 0;
}

返回值为-1,说明c<h。

注: 这里进行比较的是字符对应的ASCALL码。

(2)模拟实现

代码如下:

int str_cmp(char* arr1, char* arr2)//参数为两个char*
{
	while (*arr1 == *arr2)
	{//如果比较的两个字符相同且当某一个为“\0”时,就返回0
		if (*arr1 == '\0')
			return 0;
		arr1++;
		arr2++;
	}
//如果比较时两个不同
	if (*arr1 > *arr2)
		return 1;//arr1字符大于arr2返回1
	else if (*arr1 < *arr2)
		return -1;//arr1小于arr2返回-1
}

然后在主函数进行调用:

int main()
{
	char arr1[10] = "c";
	char arr2[10] = "h";
	int et = strcmp(arr1, arr2);//库函数
	int ret = str_cmp(arr1, arr2);//自己写的
	printf(" %d %d", ret,et);//判断我们写的与库函数的结果是否一样
	return 0;
}

很明显答案是一样的,所以我们写的没有错。 

3.strcpy

(1)介绍使用

拷贝字符串在另外一个数组里面,会覆盖原内容。

两个参数是char*,第一个参数是我们要 拷进的地址,第二个是被拷贝地址,也就是拷贝strSource的内容到strDestination。前提是strDestination的空间要大于等于strSource的空间。

举例:

int main()
{
	char arr1[10] = "abcdefg";
	char arr2[10] = "asdf";
	printf("原arr2=%s\n", arr2);
	strcpy(arr2, arr1);
	printf("新arr2=%s", arr2);
	return 0;
}

(2)模拟实现

void str_cpy(char* arr1, char* arr2)
{
	while (*arr1 = *arr2)//这里是直接将arr2赋给arr1,前提是arr2!=‘\0’
	{//依次向后移动
		arr1++;
		arr2++;
	}
}
int main()
{
	char arr1[10] = "asdfgf";
	char arr2[10] = "bfdce";
	str_cpy(arr1, arr2);
	printf("%s", arr1);
    return 0;
}


//当然这里还可以在简便一点,运行结果一样
void str_cpy(char* arr1, char* arr2)
{
	while (*arr1++ = *arr2++)
	{
		;
	}
}

结果如下:

4.strcat

(1)介绍使用

将strSource的内容连接到strDestination后面,要求前者有足够的空间容纳后者拷贝进来。

如:

int main()
{
	char arr1[20] = "abcd";
	char arr2[10] = "efghijk";
	strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

(2)模拟实现

char* str_cat(char* arr1, char* arr2)
{
	while (*arr1)//先让arr1移动到‘\0’处
	{
		arr1++;
	}
	while (*arr1++ = *arr2++)//从‘\0’处开始拷贝arr2的内容
	{
		;
	}
}

int main()
{
	char arr1[10] = "abc";
	char arr2[5] = "def";
	str_cat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

5.strstr

(1)介绍使用

 从string中查找有没有strCharSet中一样的字符串,有就返回string中这段字符串与后面的字符,如果没有就返回一个空指针(NULL);如下:

int main()
{
	char arr1[20] = "abcdebcdefgh";
	char arr2[10] = "bcdef";
	char *ret=strstr(arr1, arr2);
	printf("%s", ret);
}

(2)模拟实现

char* str_str(char* arr1, char* arr2)
{
	char* s1 = arr1;//接受接受cur中的字符,与arr1进行判断是否相等
	char* s2 = arr2;//接受arr2中的每一个字符
	char* cur = arr1;//接受arr1中的每一个字符
	while (*cur)
	{
		s1 = cur;
		s2 = arr2;
		while (*s1 && *s2 && *s1 == *s2)
		{//s1与s2都不为‘\0'且s1与s2要相等,就往后面一直查找,当遇到‘\0’就停止
			s1++;
			s2++;
		}
		if (*s2 == '\0')//如果是s2为‘\0’,说明在s1中有与s2相等的字符串,就从这里返回cur
			return cur;
		cur++;//如果s1与s2判断有不相等,然后cur就往后移动一个字符,再进行判断
	}
	return NULL;//如果判断完,s1中没有s2的字符串就返回一个空指针
}
int main()
{
	char arr1[10] = "abcbebcd";
	char arr2[5] = "bcd";
	char* ret = str_str(arr1, arr2);
	printf("%s", ret);
	return 0;
}

三、内存操作函数

头文件<string.h>

1.memcpy

(1)介绍使用

 将src以count个字节大小拷贝到dest中。如下:

int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9 ,10 };
	int a1[5] = { 0 };
	int  i = 0;
	memcpy(a1, a, 20);
	for (i = 0; i < 5; i++)
	{
		printf("%d ", a1[i]);
	}
}

 注意:count是字节单位的大小,而不是个数。

(2)模拟实现

void* my_memcpy(void* dest, const void* str, size_t count)
{
	void* ret = dest;//用于返回
	while (count--)
	{
		*(char*)dest = *(char*)str;//赋值
		dest = (char*)dest + 1;//因为dest是void型所以先转换为char*类型加1后赋给dest,下同
		str = (char*)str + 1;
	}
	return ret;
}
int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9 ,10 };
	int a2[5] = { 0 };
	my_memcpy(a2, a, 20);
	int i = 0;
	for (i = 0; i < 5; i++)
		printf("%d ", a2[i]);
	return 0;
}

但是我们这样写有一个缺点(有的编译器下使用memcpy函数也是这样):

int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9 ,10 };
	int a2[5] = { 0 };
	my_memcpy(a+2, a, 20);
	int i = 0;
	for (i = 0; i < sizeof(a)/sizeof(a[0]); i++)
		printf("%d ", a[i]);
	return 0;
}

 如果是这样,输出的结果本应该为1 2 1 2 3 4 5 8 9 10,但实际输出结果为:

所以这就略有区别,但是用memmove就不会这样,。

2.memmove

(1)介绍使用

 类型与用法与memcpy相似。

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

(2)模拟实现

 如图,我们要考虑当dest的空间在这个位置时,我们要拷贝src数据(3 4 5 6 7)时,只能从1->5方向这么去拷贝,如果从5->1拷贝就会将前面的3 4 5被覆盖,就会拷贝错误.。

 如果当dest的空间为(6 7 8 9 10)时,拷贝src数据就只能从后往前拷贝(10->6),如果从6->10就会覆盖掉6 7。

所以拷贝时当dest在src前面时就从前往后拷贝,当dest在src后面时就从后往前拷贝。

void* my_memmove(void* dest, const void* str, size_t count)
{
	void* ret = dest;//作为返回值
	if (dest < str)//从前往后拷
	{
		while (count--)
		{
			*(char*)dest = *(char*)str;
			dest = (char*)dest + 1;
			str = (char*)str + 1;
		}

	}//从后往前拷
	else
		while (count--)
		{
			*((char*)dest + count) = *((char*)str + count);
		}
	return ret;
}
int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9 ,10 };
	int i = 0;
	my_memmove(a + 2, a, 20);
	for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

 感谢阅读;多多指教

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值