【C】指针进阶补充

简介: 【C】指针进阶 回调函数,qsort函数,冒泡排序

对于前一篇内容的补充,为了后面更方便的找到此内容


8. 回调函数

回调函数就是一个通过函数指针调用的函数。

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

8.1 回调函数初步介绍

前面我们说过,下面代码改进后出现了冗余

如果我们想要进一步改进这段代码,首先就会想到把冗余的代码封装成一个函数,但是case的三个模块实现的功能是不一样的,那我们该如何进行封装呢?

如上,加 减 乘 除 需要分别调用这个函数,下面我们就要构造这个函数

如图我们将代码进行了封装,同时避免了代码的冗余

  • 最终代码:
int Add(int x, int y)
{
  return x + y;
}
int Sub(int x, int y)
{
  return x - y;
}
int Mul(int x, int y)
{
  return x * y;
}
int Div(int x, int y)
{
  return x / y;
}
void menu()
{
  printf("***************************\n");
  printf("***** 1.add    2. sub  ****\n");
  printf("***** 3.mul    4. div  ****\n");
  printf("***** 0.exit           ****\n");
  printf("***************************\n");
}
//函数名是函数地址Add,函数地址应该放到函数指针里
//指针(*p)指向的函数有两个参数(int,int),返回类型是int
void calc(int (*p)(int, int))
{
  int x = 0;
  int y = 0;
  int ret = 0;
  printf("请输入2个操作数:>");
  scanf("%d %d", &x, &y);
  ret = p(x, y);//p==(*p)前面我们介绍过,调用函数并且传参,把x,y传进去,产生的结果放到ret里
  printf("%d\n", ret);
}
int main()
{
  int input = 0;
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      calc(Add);//函数名就是函数地址,通过函数地址可以调用函数
      break;
    case 2:
      calc(Sub);
      break;
    case 3:
      calc(Mul);
      break;
    case 4:
      calc(Div);
      break;
    case 0:
      printf("退出计算器\n");
      break;
    default:
      printf("选择错误\n");
      break;
    }
  }
  while (input);
}

对回调函数定义的理解:

8.2 qsort函数

qsort函数-c语言标准库提供的排序函数

排序方式有以下几种

qsort属于快速排序q(quick)

这里我们先回顾一下之前的冒泡排序

  • 冒泡排序的代码:
#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
  int i = 0;
  //趟数
  for (i = 0; i < sz - 1; i++)//sz-1表示进行冒泡排序的趟数
  {
    //一趟冒泡排序的过程
    int j = 0;
    for (j = 0; j < sz-1-i; j++)//sz-1-i表示的是需要进行比较的对数
    {
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
}
void print(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}
int main()
{
  //冒泡排序
  //对整形数据进行排序 - 排序为升序
  int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort(arr, sz);
  print(arr, sz);
  return 0;
}

但是,bubble_sort函数只能排序我们定义的类型 - 整型,如果想更加灵活,那我们就要用到qsort函数,通过查阅相关网站我们得到用法如下:

  • 下面代码包含了void*的用法以及qsort函数的具体用法
#include <stdio.h>
//冒泡排序
void bubble_sort(int arr[], int sz)
{
  int i = 0;
  //趟数
  for (i = 0; i < sz - 1; i++)//sz-1表示进行冒泡排序的趟数
  {
    //一趟冒泡排序的过程
    int j = 0;
    for (j = 0; j < sz-1-i; j++)//sz-1-i表示的是需要进行比较的对数
    {
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
}
void print(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}
void test1()
{
  //冒泡排序
  //对整形数据进行排序 - 排序为升序
  int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort(arr, sz);
  print(arr, sz);
}
//qsort 可以排序任意类型的数据
//void qsort(void* base, //待排序数据的起始地址
//         size_t num,   //待排序数据的元素个数
//         size_t size,  //待排序数据元素的大小(单位是字节)
//         int (*cmp)(const void*, const void*) //比较2个元素大小的函数指针
//          );
//int cmp_int(const void* e1, const void* e2)
//{
//  if (*(int*)e1 > *(int*)e2)
//    return 1;
//  else if (*(int*)e1 < *(int*)e2)
//    return -1;
//  else
//    return 0;
//}
//上面代码有些多余,这里做改进,意义是一样的
int cmp_int(const void* e1, const void* e2)
{
  return (*(int*)e1 - *(int*)e2);
}
//测试qsort函数的功能
void test2()
{
  int arr[]= { 2,1,3,7,5,9,6,8,0,4 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  qsort(arr,sz,sizeof(arr[0]), cmp_int);
  print(arr, sz);
}
int main()
{
  //test1();
  test2();
  /*char ch = 'w';
  int i = 20;*/
  //void*用法
  //错误写法,类型不兼容
  //float* pf = &i;
  //pf = &ch;
  //正确写法:
  /*void* p = &ch;
  p = &i;*/
  //void*指针可以接收任意类型的指针地址
    //但是void*不能解引用操作,需要强制类型转换
  //p++;需要强制类型转换
  //错误写法
  //void* p = &i;
  //*p = 200;
  //正确写法
  //void* p = &i;
  //*(int*)p = 200;//知道i是整型,强制类型转换为int型指针
  //p++;//err,void型指针不能直接进行此操作,需要强制类型转换后才可使用
  return 0;
}

运行结果和错误如下

需要注意qsort是库函数,需要加 #include <stdlib.h>,代码中我们对cmp_int函数做了改进,其效果与之前类似,但是却减少了代码量,比较巧妙

通过代码注释中对void*的介绍,我们知道了

void*可以接收任意类型的指针
void*不能解引用操作,需要强制类型转换
void*后p++;需要强制类型转换

但是要比较的类型不同,定义cmp函数的方法也不同

  • 使用库函数,qsort排序各种类型的数据:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
void print(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}
//qsort 可以排序任意类型的数据
//void qsort(void* base, //待排序数据的起始地址
//         size_t num,   //待排序数据的元素个数
//         size_t size,  //待排序数据元素的大小(单位是字节)
//         int (*cmp)(const void*, const void*) //比较2个元素大小的函数指针
//          );
//int cmp_int(const void* e1, const void* e2)
//{
//  if (*(int*)e1 > *(int*)e2)
//    return 1;
//  else if (*(int*)e1 < *(int*)e2)
//    return -1;
//  else
//    return 0;
//}
//上面代码有些多余,这里做改进,意义是一样的
int cmp_int(const void* e1, const void* e2)
{
  return (*(int*)e1 - *(int*)e2);
}
//测试qsort函数排序整型数据
void test2()
{
  int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  qsort(arr, sz, sizeof(arr[0]), cmp_int);
  print(arr, sz);
}
struct Stu
{
  char name[20];
  int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
  return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int cmp_stu_by_age(const void* e1, const void* e2)
{
  return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//测试qsort排序结构体数据
void test3()
{
  struct Stu s[] = { {"zhangsan",20},{"lisi",55},{"wangwu",40} };
  //按照名字比较
  int sz = sizeof(s) / sizeof(s[0]);
  //qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
  qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
int cmp_char(const void* e1, const void* e2)
{
  return *(char*)e2 - *(char*)e1;
}
test4()
{
  char ch[6] = { 'a', 'b', 'c', 'd', 'e', 'f' };
  int sz = sizeof(ch) / sizeof(ch[0]);
  qsort(ch, sz, sizeof(ch[0]), cmp_char);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%c ", ch[i]);
  }
  printf("\n");
}
int main()
{
  //测试qsort函数排序整型数据
  //test2();
  //测试qsort排序结构体数据
  //test3();
  //测试排序字符数据
  test4();
  return 0;
}

如图对test3;进行监视,可以清晰地看到代码执行前后的变化,变为了升序

那么利用qsort函数的原理

如何把冒泡排序改造得对于任意类型的数据都可以排序?

  • 使用回调函数,模拟实现qsort(采用冒泡排序的方式)。
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
//用冒泡排序函数改造出一个类型于qsort的函数
//冒泡排序
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++;
  }
}
void bubble_sort2(void* base, int sz,int width,int(*cmp)(const void*e1,const void*e2))
{ 
  int i = 0;
  //趟数
  for (i = 0; i < sz - 1; i++)//sz-1表示进行冒泡排序的趟数
  {
    //一趟冒泡排序的过程
    int j = 0;
    for (j = 0; j < sz - 1 - i; j++)//sz-1-i表示的是需要进行比较的对数
    {
      if (cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
      {
        //交换
        Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
      }
    }
  }
}
void print(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}
//qsort 可以排序任意类型的数据
//void qsort(void* base, //待排序数据的起始地址
//         size_t num,   //待排序数据的元素个数
//         size_t size,  //待排序数据元素的大小(单位是字节)
//         int (*cmp)(const void*, const void*) //比较2个元素大小的函数指针
//          );
//int cmp_int(const void* e1, const void* e2)
//{
//  if (*(int*)e1 > *(int*)e2)
//    return 1;
//  else if (*(int*)e1 < *(int*)e2)
//    return -1;
//  else
//    return 0;
//}
//上面代码有些多余,这里做改进,意义是一样的
int cmp_int(const void* e1, const void* e2)
{
  return (*(int*)e1 - *(int*)e2);
}
//测试qsort函数排序整型数据
void test2()
{
  int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort2(arr, sz, sizeof(arr[0]), cmp_int);
  print(arr, sz);
}
struct Stu
{
  char name[20];
  int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
  return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int cmp_stu_by_age(const void* e1, const void* e2)
{
  return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//测试qsort排序结构体数据
void test3()
{
  struct Stu s[] = { {"zhangsan",20},{"lisi",55},{"wangwu",40} };
  //按照名字比较
  int sz = sizeof(s) / sizeof(s[0]);
  //qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
  bubble_sort2(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
int cmp_char(const void* e1, const void* e2)
{
  return *(char*)e2 - *(char*)e1;
}
test4()
{
  char ch[6] = { 'a', 'b', 'c', 'd', 'e', 'f' };
  int sz = sizeof(ch) / sizeof(ch[0]);
  bubble_sort2(ch, sz, sizeof(ch[0]), cmp_char);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%c ", ch[i]);
  }
  printf("\n");
}
int main()
{
  //测试qsort函数排序整型数据
  //test2();
  //测试qsort排序结构体数据
  //test3();
  //测试排序字符数据
  test4();
  return 0;
}

  • 要点
    这些代码都不会脱离规定

qsort 可以排序任意类型的数据 void qsort(void base, //待排序数据的起始地址
size_t num, //待排序数据的元素个数
size_t size, //待排序数据元素的大小(单位是字节)
int (cmp)(const void, const void
) //比较2个元素大小的函数指针

);**

  • 结语:

    铁铁们,介绍到这里本章内容就结束了,如果小伙伴还有不理解的内容,也不要担心,毕竟学习是一个循序渐进的过程嘛
    文章中某些内容我们之前有介绍,所以只是一笔带过,还请谅解。
    希望以上内容对大家有所帮助👀,如有不足望指出🙏
相关文章
|
18天前
|
存储
指针进阶详解(下)
指针进阶详解(下)
23 0
|
5天前
|
C语言
C语言进阶:进阶指针(下)
C语言进阶:进阶指针(下)
|
5天前
|
C语言
C语言进阶:指针的进阶(上)
C语言进阶:指针的进阶(上)
|
6天前
入门后指针进阶习题深度分析
入门后指针进阶习题深度分析
17 1
|
6天前
进阶指针笔试题
进阶指针笔试题
15 0
|
11天前
|
存储 C语言 C++
C语言进阶——指针
C语言进阶——指针
|
13天前
|
C语言
C语言进阶⑬(字符串函数)+(指针编程题)strlen+strcpy+strcat+strstr+strtok+strerror(下)
C语言进阶⑬(字符串函数)+(指针编程题)strlen+strcpy+strcat+strstr+strtok+strerror
6 0
|
13天前
|
安全 C语言
C语言进阶⑬(字符串函数)+(指针编程题)strlen+strcpy+strcat+strstr+strtok+strerror(中)
C语言进阶⑬(字符串函数)+(指针编程题)strlen+strcpy+strcat+strstr+strtok+strerror
23 0