『C语言进阶』指针进阶(二)

简介: 『C语言进阶』指针进阶(二)

前言

在上篇指针进阶中,我们对字符指针、指针数组、数组指针以及数组传参和指针传参有了一定的了解,你以为指针进阶就只有这些内容嘛?不不不,接下来,小羊将继续完善指针进阶内容,坐好小板凳准备上课了~~~

一、函数指针

1.1 函数指针的定义

函数指针,顾名思义,就是一个指向函数的指针

上篇中我们学到

整形指针是接收整形的地址

字符指针是接收字符的地址

数组指针是接收数组的地址

那么函数有地址吗?函数名又表示什么呢?

答案是:函数是有地址的,地址是函数名或者&函数名,解引用时,p和*p都可以

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int a = 1, b = 2;
  int c = Add(a, b);
  printf("%d\n", c);
  printf("%p\n", &Add);
  printf("%p\n", &Add);
  return 0;
}

运行结果:

3
00007FF750191348
00007FF750191348

由此可见,函数名也可以表示函数的地址

所以问题来了,我们以数组指针为例:

写一个指向int arr[10]数组的数组指针

第一步:
(*p)    //先确定是一个指针
第二步:
(*p)[10]  //确定指向的是一个有10个元素的数组
第三步:
int(*p)[10] //确定该数组元素为int型
第四步:
int(*p)[10]=&arr;//将数组的地址赋值给数组指针
//或者int(*p)[10]=arr

我们照着数组指针的例子来写一个函数指针:指向int add(int x,int y)

第一步:
(*p)      //先确定是一个指针
第二步:
(*p)(int,int) //确定指向的函数有两个参数
第三步:
int (*p)(int,int) //确定该函数的返回类型
第四步:
int (*p)(int,int)=&add;//将函数的地址赋值给函数指针
//等价于:int (*p)(int,int)=add;

1.2 函数指针调用

1.1中得到 &函数名==函数名 ,所以函数指针的解引用调用可以不写*,也可以不写*。

既然我们现在知道函数指针是怎么写的,那么函数指针有什么用呢?

数组指针可以用来访问数组,那函数指针当然也就是调用函数的了,

我们还是以数组指针为例:

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5 };
  int(*p)[10] = arr;
  for (int i = 0; i < 5; i++)
  {
    printf("%d ", (*p)[i]);
  }
  printf("\n");
  return 0;
}

那现在用函数指针调用函数:

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int a = 1, b = 2;
  int(*p1)(int, int) = &Add;
  int(*p2)(int, int) = Add;
  int tmp1 = Add(a, b);
  int tmp2 = (*p1)(a, b);//写法一
  int tmp3 = (p2)(a, b);//写法二
  printf("tmp1=%d\ntmp2=%d\ntmp3=%d", tmp1, tmp2, tmp3);
  return 0;
}

运行结果:

tmp1=3
tmp2=3
tmp3=3

1.3 有趣的代码

一、

(*(void(*)())0)();
//先分解
一、void(*)()是一个无参无返回类型的函数指针
二、(void(*)())0是将0强制类型转换,0原本是int类型,被强制转换为void(*)()函数指针
三、*(void(*)()0)这是将0转换为函数指针后的解引用操作
四、(*(void(*)()0)()此时0就是一个函数的地址,可以看成(*0)(),意思就是解引用一个函数地址并且调用

二、

void(*signal(int,void(*)(int)))(int);
我先一步一步分解给你们看
void(*)(int)
signal(int,void(*)(int))
signal(int,void(*)(int))
void(*signal(int,void(*)(int)))(int)
signal先和(int,void(*)(int))结合,说明它是一个函数,参数为int,void(*)(int),现在我们明确了它的函数名和参数,还需要知道它的返回类型,先看一个函数,int Add(int),这个函数的函数名是Add,参数是int,把函数名和参数去掉后就是返回类型,同样,把这里的函数名和参数去掉,void(*)(int)这个就是它的返回类型

上面代码是一个函数调用


二、函数指针数组

2.1 函数指针数组的定义

函数指针数组,存放函数指针的数组,每一个元素都是函数指针类型

先写出函数指针
int(*p)(int,int)
改成数组
int(*p[10])(int,int)

例:

#include<stdio.h>
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;
}
int main()
{
  int(*pf1) (int, int) = Add;
  int(*pf2) (int, int) = Sub;
  int(*pf3) (int, int) = Mul;
  int(*pf4) (int, int) = Div;
  int (*arr[4])(int, int) = { Add,Sub,Mul,Div };
  int sz = sizeof(arr) / sizeof(arr[0]);
  for (int i = 0; i < sz; i++)
  {
    printf("%d\n", arr[i](7, 4));
  }
  return 0;
}

2.2 实战操作

用C语言制作简易计算器

#include<stdio.h>
void menu()
{
  printf("****************************\n");
  printf("*****  1.Add    2.Sub  *****\n");
  printf("*****  3.Mul    4.Div  *****\n");
  printf("*****  0.exit          *****\n");
  printf("****************************\n");
}
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;
}
int main()
{
  int input = 0;
  int x = 0, y = 0;
  int (*arr[5])(int, int) = { 0,Add,Sub,Mul,Div };
  do 
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    if (input >= 1 && input <= 4)
    {
      printf("请输入两个操作数:>");
      scanf("%d %d", &x, &y);
      printf("%d\n", arr[input](x, y));
    }
    else if (input == 0)
    {
      printf("退出计算机");
      break;
    }
    else
      printf("选择错误\n请重新选择:>\n");
  } while (input);
  return 0;
}

三、函数指针数组指针

指向函数指针数组的指针,是一个指针,指针指向存放函数指针的数组,该数组成员都是函数指针

先写出函数指针
int (*p)(int,int)
改写成函数指针数组
int(*p[10])(int,int)
最后写成函数指针数组指针
int(*(*p)[10]))(int,int)

示例:

#include<stdio.h>
void menu()
{
  printf("****************************\n");
  printf("*****  1.Add    2.Sub  *****\n");
  printf("*****  3.Mul    4.Div  *****\n");
  printf("*****  0.exit          *****\n");
  printf("****************************\n");
}
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;
}
int main()
{
  int (*pa)(int, int) = Add;//函数指针
  int (*arr[4])(int, int) = { Add };//函数指针数组,存放函数指针的数组
  int (*(*ppa)[4])(int, int) = &arr;//函数指针数组指针,存放函数指针数组地址的指针
  return 0;
}

四、回调函数

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

示例:

#include<stdio.h>
void menu()
{
  printf("****************************\n");
  printf("*****  1.Add    2.Sub  *****\n");
  printf("*****  3.Mul    4.Div  *****\n");
  printf("*****  0.exit          *****\n");
  printf("****************************\n");
}
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 Calc(int (*pf)(int, int))
{
  int x = 0, y = 0;
  printf("请输入两个操作数:>");
  scanf("%d %d", &x, &y);
  int ret = pf(x, y);
  printf("%d\n", ret);
}
int main()
{
  int input = 0;
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case 0:
      printf("退出计算机\n");
      break;
    case 1:
      Calc(Add);
      break;
    case 2:
      Calc(Sub);
      break;
    case 3:
      Calc(Mul);
      break;
    case 4:
      Calc(Div);
      break;
    default:
      printf("输入错误\n");
      break;
    }
  } while (input);
  return 0;
}

4.1 排序

冒泡排序,相邻两个两个的比较并交换位置,在C语言初阶数组中,详细讲解了关于冒泡排序的知识点,忘记了的铁汁们可以看一下

代码展示:

#include <stdio.h>
void Print(int arr[], int sz)
{
  for (int i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
}
void bubble_sort(int arr[],int sz)
{
  int i = 0;
  for (i = 0; i < sz - 1; i++)
  {
    int j = 0;
    for (j = 0; j < sz - i - 1; j++)
    {
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
}
int main()
{
  int arr[] = { 5,3,7,6,1,8,9,2,4,0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort(arr,sz);
  Print(arr, sz);
  return 0;
}

4.2 qsort部分展示

冒泡排序只能排序整形,而qsort函数,内部采用快速排序,可以排序各种类型的数据,接下来展示qsort排序部分类型的方法.

qsort是一个库函数,快速排序的方法来实现的, 头文件是<stdlib.h>

qsort库函数,void qsort( void *base, size_t num, size_t width, int (_cdecl *compare )

(const void *elem1, const void *elem2 ) );传入的参数,一个是指针,一个整形,一个整形一个函数指针,base 数组首元素(就是数组名),num数组里有多少个元素,width每个元素的大小(单位是字节),compare比较两个指针指向的元素,小于 输出小于0的元素,等与 输出0,大于 输出大于0的元素 排序任意类型

示例:

qsort函数部分应用

分别将元素比较方法int_cmp和char_cmp的指针(地址) 传给 qsort函数.由qsort函数调用这些比较函数

#include <stdlib.h>
#include <stdio.h>
int int_cmp(const void* e1, const void* e2)//整形元素排序方法
{
  return *(int*)e1 - *(int*)e2;
}
int char_cmp(const void* e1, const void* e2)//字符型元素排序方法
{
  return *(char*)e1 - *(char*)e2;
}
int main()
{
  int arr1[10] = { 4,5,1,8,9,2,10,3,7,6 };
  char arr2[] = "fbadegc";
  int sz1 = sizeof(arr1) / sizeof(arr1[0]);
  int sz2 = sizeof(arr2) / sizeof(arr2[0]);
  qsort(arr1,sz1,sizeof(arr1[0]),int_cmp);
  for (int i=0; i < sz1; i++)
  {
    printf("%d ", arr1[i]);
  }
  printf("\n");
  qsort(arr2, sz2, sizeof(arr2[0]), char_cmp);
  for (int i = 0; i < sz2; i++)
  {
    printf("%c ", arr2[i]);
  }
  printf("%s", arr2);
  return 0;
}

运行结果:

1 2 3 4 5 6 7 8 9 10
 a b c d e f g

qsort函数用冒泡排序的模拟实现,以及各种类型的排序咱们下篇一起学习~

希望这篇文章对铁汁们有所帮助,咱们下期再见!

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

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等