【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快排算法的补充

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

相关文章
|
2月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
60 0
|
23天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
76 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
23天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
47 9
|
23天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
42 7
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
120 13
|
27天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
27天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
96 3
|
28天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
1月前
|
存储 C语言 开发者
C 语言指针与内存管理
C语言中的指针与内存管理是编程的核心概念。指针用于存储变量的内存地址,实现数据的间接访问和操作;内存管理涉及动态分配(如malloc、free函数)和释放内存,确保程序高效运行并避免内存泄漏。掌握这两者对于编写高质量的C语言程序至关重要。
56 11
|
27天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
37 1