赞
踩
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
那么回调函数有什么实际性的作用呢?下面模拟库函数qsort来顺便了解回调函数的用法:(在这里使用冒泡排序来模拟)首先要了解什么是qsort:
上面的意思是:对数组的元素进行排序,使用函数确定顺序所排序的类型。依然从代码入手:
- int my_cmp(const void* e1, const void* e2)
- {
- return *(int*)e1 - *(int*)e2;
- }
-
- void Swap(char* buf1, char* buf2, size_t width)
- {
- for (size_t i = 0; i < width; i++)
- {
- char tmp = *buf1;
- *buf1 = *buf2;
- *buf2 = tmp;
- buf1++;
- buf2++;
- }
- }
-
- Bubble_Sort(void* base,size_t sz,size_t width,int (*cmp)(const void* e1,const void* e2))
- {
- for (size_t i = 0; i < sz - 1; i++)
- {
- for (size_t j = 0; j < sz - i - 1; j++)
- {
- if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
- {
- Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
- }
- }
- }
- }
-
- void test1()
- {
- int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
- int sz = sizeof(arr) / sizeof(arr[0]);
- Bobble_Sort(arr, sz, sizeof(arr[0]), my_cmp);
- for (int i = 0; i < sz; i++)
- {
- printf("%d ", arr[i]);
- }
- }
- int main()
- {
- test1();
- return 0;
- }

如上所示:我们要想将arr数组排为升序,在模拟实现时,要给四个参数:
1、这个数组名
2、这个数组的元素个数
3、这个数组每个元素的大小(单位字节)
4、指向比较两个元素的函数的指针。(这个来控制是比较整型或者浮点型或者结构体等等)
接收的时候用void*接受是因为不知道我传过来的是什么类型的,在之后强制类型转即可。
上述代码中:my_cmp为我要排序整型所需要的函数。Swap为交换两个元素Bubble_Sort为冒泡排序模拟库函数qsort在其中:if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}这行代码是最核心的,width为偏移量,这样可以通过 回调函数来调用my_cmp来知道我是什么类型的和大小。将传过来的数组名用void* base接收,后来转换为(char*)这样可以+偏移量来找到每个元素,这也是要传数组每个元素的大小的意义。在Swap函数中与传统的交换不同的是需要每一个元素的大小,这样可以将一个元素以字节为单位一个一个交换,毕竟如果直接交换的话,就会将代码写死(比如写了个int类型,就不能交换浮点数类型)
在上述代码中就在函数中调用了函数,所以就用了回调函数cmp
首先来了解一下有那些处理字符和字符串的库函数:
这是用来计算字符串长度的。
- //计数
- int my_strlen1(const char* src)
- {
- assert(&src);
-
- int count = 0;
- while (*src++)
- count++;
- return count;
- }
- //递归
- int my_strlen2(const char* src)
- {
- assert(src);
-
- if (*src != '\0')
- return 1 + my_strlen2(src + 1);
- else
- return 0;
- }
- //指针-指针
- int my_strlen3(const char* src)
- {
- assert(src);
-
- const char* dest = src;
- while (*dest)
- {
- dest++;
- }
- return dest - src;
- }
-
- int main()
- {
- char arr[] = "abcdefg";
- int ret = my_strlen3(arr);
- printf("%d\n", ret);
- return 0;
- }

如上用了三种方法分别模拟了字符串长度的计算。
这是将一个字符串拷贝到另外一个字符串中的
- char* my_strcpy(char* dest, char* src)
- {
- char* ret = dest;
- assert(dest && src);
- while (*dest++ = *src++)
- {
- ;
- }
- return ret;
- }
-
- int main()
- {
- char arr1[] = "abcdefg";
- char arr2[20] = "";
- my_strcpy(arr2, arr1);
- printf("%s\n", arr1);
- printf("%s\n", arr2);
- return 0;
- }

在模拟实现中将src一个个赋给dest,最后返回目标数组的地址
这是将一个字符串追加在另外一个字符串后面。
- char* my_strcat(char* dest, char* src)
- {
- assert(dest && src);
- char* ret = dest;
- //1.找到要追加的地方
- while (*dest)
- {
- dest++;
- }
- //2.追加
- while (*dest++ = *src++)
- {
- ;
- }
- return ret;
- }
-
- int main()
- {
- char arr1[20] = "hello ";
- char arr2[] = "ppr";
- my_strcat(arr1, arr2);
- printf("%s\n", arr1);
- return 0;
- }

首先找到我要追加的地方,然后进行赋值即可,最后返回被追加的数组首元素。
这是比较两个字符串的大小。
如上,如果第一个字符串小于后一个,那么返回一个小于0 的数;
如果第一个字符串等于后一个,那么返回0 ;
如果第一个字符串大于后一个,那么返回一个大于0 的数。
(在VS编译器中分别返回1,0,-1)。
- int my_strcmp( char* str1, char* str2)
- {
- assert(str1 && str2);
- while (*str1 == *str2)
- {
- if (*str1 == '\0')
- return 0;
- str1++;
- str2++;
- }
- if (*str1 > *str2)
- return 1;
- else
- return -1;
- //return *str1 - *str2;
- }
-
- int main()
- {
- char arr1[20] = "abqf";
- char arr2[20] = "abqf";
- int ret = my_strcmp(arr1, arr2);
- printf("%d\n", ret);
- return 0;
- }

这些长度受限制的函数引入是因为strcpy函数不安全,
比如在strcpy函数中,如果拷贝过去字符串,此时接收这个字符串的数组不够这么大,就会失败,同样,如果c语言中最开始不声明“#define _CRT_SECURE_NO_WARNINGS 1”就会不可以使用,所以就衍生出了strncpy,这个作用和strcpy几乎是一模一样的,就是多了1个无符号整型参数,作用是我要拷贝过来几个字符。
但是strncpy只是相对于strcpy安全罢了,如果你输入的数字等于这个数组的最大元素,那么就会使‘\0’拷贝不过来,也会出现错误:
所以strncpy只是相对于strcpy安全。
这个多出来的无符号整型的参数就是我需要追加几个字符数。
这个多出来的无符号整型的参数就是我需要比较几个字符数。
这个函数的作用是在一个字符串中找另外一个字符串。
- char* my_strstr(const char* src1, const char* src2)
- {
- assert(src1 && src2);
- if (*src2 == '\0')
- return (char*)src1;
-
- const char* s1 = src1;
- const char* s2 = src2;
- const char* p= src1;
-
- while (*p)
- {
- s1 = p;
- s2 = src2;
-
- while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
- {
- s1++;
- s2++;
- }
- if (*s2 == '\0')
- return (char*)p;
- p++;
- }
- return NULL;
- }
-
- int main()
- {
- char arr1[] = "abbbbcdef";
- char arr2[] = "bbc";
- char* arr3 = my_strstr(arr1, arr2);
- if (arr3 == NULL)
- {
- printf("找不到\n");
- }
- else
- {
- printf("%s\n", arr3);
- }
- return 0;
- }

思路:
在src1中找src2,第一个while循环将s1,s2分别指向src1和src2的第一位(这样可以使得每次找完可能相等的位置后,如果不相等就会返回此时记录的位置),之后来找可能相等的位置,在第二个while循环中来看看是否相等。
这是属于一个字符串的分割,
第一个参数为我所需要分割的起始位置,
在后续的调用中在第一个参数位置只需传空指针即可。
第二个参数中传我所分割的标志集合。
这是一个打印错误信息的函数,C语言的库函数在运行的时候,如果发生错误,就会将错误码存在一个变量中,这个变量是:errno(在errno.h这个头文件里面)
错误码是一些数字:1 2 3 4 5
我们需要将错误码翻译成错误信息
- int main()
- {
- FILE* pf = fopen("test.txt", "r");
- if (pf == NULL)
- {
- printf("%s\n", strerror(errno));
- //perror("fopen");
- return 1;
- }
- //读文件
- //关闭文件
- fclose(pf);
- pf = NULL;
-
- return 0;
- }

在上面代码中,在当前路径下读一个叫做test.txt的文件,如果没有就会将错误代码打印出来
拓展:有另外一个函数perror这个用起来更加顺手些,将可以函数错误信息直接打印出来。
可以理解为:printf + strerror
这个函数也是拷贝,但是不只局限于字符串的拷贝,属于内存的拷贝,
第一个参数是目标拷贝函数,第二个参数是待拷贝的函数,第三个参数是拷贝多少个字节
- int main()
- {
- int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
- int arr2[20] = { 0 };
- memcpy(arr2, arr1, 24);
- return 0;
- }
模拟实现:
- void* my_memcpy(void* dest, void* src, size_t num)
- {
- void* ret = dest;
- while (num--)
- {
- *(char*)dest = *(char*)src;
- ++(char*)dest;
- ++(char*)src;
- }
- return ret;
- }
-
-
- int main()
- {
- int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
- int arr2[20] = { 0 };
- my_memcpy(arr2, arr1, 24);
- return 0;
- }

我们这个自己模拟的局限性:不能够自我拷贝(但是库函数里的memcpy可以),那么接下来进行优化---------引入了memmove函数的模拟实现
解决方法:如下图,若src在dest后面,就将src从前往后拷贝,若src在dest前面就从后往前拷贝。
- void* my_memmove(void* dest, void* src, size_t num)
- {
- assert(dest && src);
- void* ret = dest;
- if (dest < src)
- {
- //前->后
- while (num--)
- {
- *(char*)dest = *(char*)src;
- dest = (char*)dest + 1;
- src = (char*)src + 1;
- }
- }
- else
- {
- //后->前
- while (num--)
- {
- *((char*)dest + num) = *((char*)src + num);
- }
- }
-
- }
-
- int main()
- {
- int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
- my_memmove(arr1+2, arr1, 20);
- return 0;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。