当前位置: 代码迷 >> 综合 >> 模拟实现qsort、strlen、strcpy、strcat、strcmp、strstr、memcpy、memmove函数
  详细解决方案

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

热度:0   发布时间:2023-12-16 17:51:51.0

目录

一、快速排序 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”时,就返回0if (*arr1 == '\0')return 0;arr1++;arr2++;}
//如果比较时两个不同if (*arr1 > *arr2)return 1;//arr1字符大于arr2返回1else 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相等的字符串,就从这里返回curreturn 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;}}//从后往前拷elsewhile (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;
}

 感谢阅读;多多指教