【C语言进阶】指针的进阶(下)

简介: 【C语言进阶】指针的进阶(下)

前言

上篇文章,我们学习了字符指针、指针数组、数组指针、函数指针,接下来我们来一起探讨更深层次的指针进阶的内容!!!

一、函数指针数组

1.1认识函数指针数组

前面我们学到了函数指针,函数指针是存放函数地址的指针,其表现形式为:

int*(*pa)(int,int)= &add        表示将add函数的地址传递给pa这个函数指针,这个函数的形参为两个int类型,返回参数为int类型的数据,这就是函数指针。

应用:

1.int len =(*pa)(2,1)        表示的是使用add函数传参为2,1 然后返回的数值给 len

2.int len = pa(2,1)             因为pa其实就是得到函数的地址,正常函数的使用的时候,也是函数名加传参,函数名的地址和取地址函数名的地址是一样的,所以是可以不需要解引用(*pa),这个星号是无所谓的,即使是******pa也不会影响结果

接下来,是对于函数指针数组的应用

数组是存放相同类型数据的存储空间,我们已经学习了指针数组

如:

    int*arr[10]       表示存放十个整型指针的数组arr

所以我们可以打开猜想一下,函数指针数组应该是什么样的呢?

1.2函数指针数组的应用

//函数指针数组的用途: 转移表(相当于一个中介,连接一个函数到另一个函数)

如何转换,转换的是什么?步骤是什么?

演示:

二、指向函数指针数组的指针

指向函数指针数组的指针是一个指针

指针指向一个数组,数组的元素都是函数指针

void test(const char* str)
{
  printf("%s\n", str);
}
int main()
{
  //函数指针pfun
  void (*pfun)(const char*) = test;
  //函数指针的数组pfunArr
  void (*pfunArr[5])(const char* str);
  pfunArr[0] = test;
  //指向函数指针数组pfunArr的指针ppfunArr
  void (*(*ppfunArr)[5])(const char*) = &pfunArr;
  //(*ppfunArr)  ppfunArr与*先结合,说明这是一个指针,剩下void (*[5])(const char*)  实际上是 void (*)(const char*) [5]函数指针数组
  //所以 void(*(ppfunArr)[5])(const char*) 这是个指向函数指针数组的指针
  return 0;

我们可以发现,(ppfunArr)与【】先结合的时候这是函数指针数组,当与*先结合的时候,这就是指向函数指针数组的指针,确定或者使用这一类的指针数组、数组指针,第一步是确认ppfunArr先和谁结合,确定到底是数组还是指针,然后向外刨析

我们可以发现,(ppfunArr)与【】先结合的时候这是函数指针数组,当与*先结合的时候,这就是指向函数指针数组的指针

判别这一类类型的方法:

       1.确认ppfunArr先和谁结合,确定到底是数组还是指针

      2.如果是【】说明这个整体是数组,如果是 * 说明整体是指针

      3.然后向外刨析,一个括号一个括号的破开

       4.void(*pa)()实际上是 void(*)()  pa  前者为函数类型,后者pa是指针名 

三、回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

#include<stdio.h>
int sub(int x, int y) {
  return  x - y;
}
int add(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 coul(int(*pa)(int, int)) {
  int x, y;
  scanf("%d %d", &x, &y);
  int len=pa(x, y);
  printf("%d\n", len);
}
int main()
{ //函数指针 pa
  int(*pa)(int, int) = add;
  //函数指针数组
  int(*ppa[10])(int, int) = { add,sub,mul,div };
  //ppa[10]  ppa 先于[]相结合 说明这个是个数组 剩下元素为 int(*)(int,int)说明存放的是函数指针类型
  //所以这就是可以存放十个以两个形参为int类型,返回类型为int的,函数指针数组,ppa(数组名)
  //对于函数指针数组的应用
  int x, y;
  int input = 1;
  int ret = 0;
  do {
    printf("*************************\n");
    printf(" 1:add       2:sub \n");
    printf(" 3:mul       4:div \n");
    printf("*************************\n");
    printf("请选择:");
    scanf("%d", &input);
    switch (input) {
    case 1:
      coul(add);  //使用回调函数,使得main函数与要实现的add或者sub等函数建立起来桥梁
      break;
    case 2:
      coul(sub);
      break;
    case 3:
      coul(mul);
      break;
    case 4:
      coul(div);
      break;
    case 0:
      printf("退出程序\n");
      break;
    default:
      printf("选择错误\n");
      break;
    }
  } while (input);
  return 0;
}

3.1qsort函数

3.1.1qsort函数的定义

qsort函数的使用,需要 <stdio.h><search.h>两个头文件

需要传递的数据为:

1.void* base          目标数据(需要排序的)

2.size_t num         排序的数据的元素个数,如arr[10],就传参为10

3.size_t width       排序数据的元素所占字节的大小,如 int arr[10],传参为4

4.int (__cdecl *compare )(const void *elem1, const void *elem2 )

  传递的是函数指针,实参为两个被const修饰的无符号指针类型,void*是为了普适性,可     以接收所有类型的数据

接下来是代码实现:

#include<stdio.h>
#include<search.h>
int compare(const void* e1, const void* e2) {
  //使用 const是防止修改e1,e2的指针指向的地址,void*(无符号指针)表示函数的普适性
  //    可以接收所有类型的指针。
  return *(int*)e2 - *(int*)e1;
  //(int*)是因为是对void*类型进行强制转换,然后*解引用,比较e1 和 e2 的大小
}
int main()
{
  int arr[] = { 1,2,31,45,55,6,7,8,9,10,0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  qsort(arr, sz, sizeof(arr[0]), compare);
  for (int i = 0; i < sz; i++) {
    printf("%d ", arr[i]);
  }
  return 0;
}

3.1.2使用回调函数,模拟实现qsort(冒泡的方式)

//使用回调函数,模拟实现qsort(采用冒泡的方式)
//比较函数
int compare(const void* e1, const void* e2) {
  return *(int*)e1 - *(int*)e2;
}
//交换函数
void swap(void* e1, void* e2, int width) {
  for (int i = 0; i < width; i++) {
    //一个元素 width个字节,自然是width次转换
    char tmp = (*((char*)e1 + i));
    (*((char*)e1 + i)) = (*((char*)e2 + i));
    (*((char*)e2 + i)) = tmp;
  }
}
//使用冒泡排序的方式模拟实现qsort函数
void buffer_qsort(void* base, size_t num, size_t width, int (*compare)(const void* e1, const void* e2)) {
  for (int i = 0; i < num-1; i++) {
    for (int j = 0; j < num - 1 - i; j++) {
      //冒泡排序基本的循环是不用变的,只需要改变交换的方式
      if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {
        //传递数据的适合因为不知道传参的是什么类型,所以就是用表示一个字节的char*进行强制类型转换
        //(char*)base  取的是数组的首元素的第一个字节,然后加上j*width,相当于跳过该类型的元素,找到下一个元素
        //进行compare函数比较
        //swap函数进行交换
        swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
      //除了传递每一个元素(char*)base + j * width 也要传递一个数组元素字节为多少 width
        //我们的交换原理就是创建一个临时变量 width次循环交换 
      }
    }
  }
}
int main()
{
  int arr[] = { 11,2,3,4,45,62,7,8,8,9 };
  int sz = sizeof(arr) / sizeof(arr[1]);
  buffer_qsort(arr, sz, sizeof(arr[1]), compare);
  for (int i = 0; i < sz; i++) {
    printf("%d ", arr[i]);
  }
  return 0;
}

3.1.3qsort算法的实现

具体介绍,请看博主主页快排那一章的内容,下面仅仅是演示快排算法

void quick_sort(int* arr, int l, int r) {
  if (l >= r) {
    return;
  }
  int k = arr[l];
  int i = l - 1;
  int j = r + 1;
  while (i < j) {
    do {
      i++;
    } while (arr[i] < k);
    do{
      j--;
    } while (arr[j] > k);
    if (i < j) {
      int tmp = arr[i];
      arr[i] = arr[j];
      arr[j] = tmp;
    }
  }
  quick_sort(arr, l, j);
  quick_sort(arr, j + 1, r);
}
int main()
{
  int arr[] = { 1,25,3,4,5,6,7,8,9,10 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  quick_sort(arr, 0, sz-1);
  for (int i = 0; i < sz; i++) {
    printf("%d ", arr[i]);
  }
  return 0;
}

总结

那么到此为止,指针进阶的内容就结束啦,完结撒花!!!

我们在这边学习了,字符数组指针数组数组指针函数指针函数指针数组指向函数指针数组的指针,以及回调函数的定义,和通过冒泡排序模拟实现qsort函数讲解qsort函数的原理,以及对于quick_sort快排算法的补充

博主提前预告哦,接下来是对于字符串的一系列函数的学习,让我们拭目以待!!!

相关文章
|
5天前
|
C语言
c语言指针总结
c语言指针总结
15 1
|
3天前
|
存储 安全 编译器
C语言详解指针(指针海洋的探索,将传值与传址刻在心里)
C语言详解指针(指针海洋的探索,将传值与传址刻在心里)
8 0
|
5天前
|
C语言
C语言(指针详解)重点笔记:指针易错点,都是精华
C语言(指针详解)重点笔记:指针易错点,都是精华
12 0
|
5天前
|
存储 C语言
C语言指针讲解(适用于初学者)
C语言指针讲解(适用于初学者)
6 0
|
5天前
|
搜索推荐 C语言
详解指针进阶2
详解指针进阶2
|
5天前
|
存储 程序员 C语言
【C 言专栏】C 语言指针的深度解析
【4月更文挑战第30天】C 语言中的指针是程序设计的关键,它如同一把钥匙,提供直接内存操作的途径。指针是存储其他变量地址的变量,通过声明如`int *ptr`来使用。它们在动态内存分配、函数参数传递及数组操作中发挥重要作用。然而,误用指针可能导致错误,如空指针引用和内存泄漏。理解指针的运算、与数组和函数的关系,以及在结构体中的应用,是成为熟练 C 语言程序员的必经之路。虽然挑战重重,但掌握指针将增强编程效率和灵活性。不断实践和学习,我们将驾驭指针,探索更广阔的编程世界。
|
5天前
|
存储 C语言
C语言进阶---------作业复习
C语言进阶---------作业复习
|
5天前
|
存储 Linux C语言
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-2
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
5天前
|
自然语言处理 Linux 编译器
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-1
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)