C语言之指针进阶篇(2)

简介: C语言之指针进阶篇(2)

今天我们继续更深入的来了解指针!主要是指针&函数&数组的点!

函数指针

在前面我们已经了解过字符指针,数组指针,这里我们将介绍到函数指针!

指针数组——是数组,是存放指针的数组。

数组指针——是指针,指向数组的指针。

字符指针——指向字符的指针。

整型指针——指向整型的指针。

浮点型的指针——指向浮点型的指针。

函数指针——指向函数的指针。

函数名和&函数名

类比数组指针。

数组指针——指向数组的指针——存放的是数组的地址——&数组名就是数组的地址

函数指针——指向函数的指针——存放的是函数的地址——函数地址是否是&函数名呢?


那我们用代码来验证下。

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  //int x = 0;
  //int y = 0;
  //scanf("%d %d", x, y);
  printf("%p\n", &Add);
  printf("%p\n", Add);
  //&函数名 就是函数的地址
  //函数名 也是函数的地址
  //注意函数名并没有首元素这一说法
}

&函数名和函数名,都是函数的地址。

那它们是否有什么区别和它们的类型是一样的吗?

它们没有区别,知识写法不一样。

它们两个类型是一样的。指向同一个函数,类型是相同的。

整形函数指针类型?


函数指针的定义

如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名和&函数名均表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

那该怎样去写函数指针呢?

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  printf("%p\n", &Add);
  printf("%p\n", Add);
  int (*p)(int,int) = &Add;//更容易理解和熟悉
  int (*p)(int x,int y) = &Add;
  int (*p)(int x,int y) = Add;//写不写x y都可
  int (*p)(int,int) = Add;
//&和x y 写不写都可
}
这个语句就定义了一个指向函数的指针变量 p。
首先它是一个指针变量,所以要有一个“*”,即(*p);
其次前面的 int 表示这个指针变量可以指向返回值类型为 int 型的函数;
后面括号中的两个 int 表示这个指针变量可以指向有两个参数且都是 int 型的函数。
所以合起来这个语句的意思就是:
定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。
p 的类型为 int(*)(int,int)。

int (*p) (int, int);


函数返回值类型 (* 指针变量名) (函数参数列表);


定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int(*)(int,int)。


p就是函数指针变量。


那如果写成int* p(int,int);?


*和int 结合 int* 变成了函数声明了。  

函数指针的使用

函数指针的使用最长应用在转移表和回调函数  ,那我们在接下来的文章都会讲到。

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  printf("%p\n", &Add);
  printf("%p\n", Add);
  int (*p)(int,int) = &Add;
  int ret=(*p)(3, 5);//解引用找到函数
  int ret = (********p)(3, 5);//*是个摆设,写不写都可
  int ret = Add(3, 5);//直接调用Add函数就是用Add函数的地址
  int ret = p(3, 5);//这里也可以直接使用地址
  printf("%d", ret);
}

我们有以上三种写法去调用函数 Add,但是我们不可以乱写哦!

int ret=* p(3, 5);

函数指针陷阱

阅读两段有趣的代码!

我们改怎样去理解这两端代码呢?

代码1

(*(void (*)())0)();
//函数调用

  • 在调用 0地址处的函数,这个函数没有参数,返回类型是void
  • 把0从int类型强制转化成函数指针类型,指向了0处的地址处的函数


代码2

void (*signal(int , void(*)(int)))(int);
//函数声明

  • 这个代码是一次函数声明,声明的是signal函数
  • signal函数有两个参数,一个是int类型,一个是函数指针类型,该类型是void (*)(int)
  • 函数指针类型 该函数指针指向的函数,参数是int, 返回类型是void
  • signal函数的返回类型也是函数指针类型,该类型是void (*) (int)
  • 函数指针类型 该函数指针指向的函数,参数是int, 返回类型是void


代码2太复杂了,那能不能简化代码2???当然可以。

void (* signal(int, void(*)(int)))(int);
//void(*)(int) signal(int void(*)(int));❌
//重定义
typedef void(*pfun_t)(int);
pfun_t signal(int pfun_t);

注 :推荐《C陷阱和缺陷》


注意

  1. 函数指针变量&函数指针&函数指针类型
  2. 函数指针类型 修饰一个 函数指针变量(可以是函数等 / 也可以没有/或者 将其他类型强制转化成函数指针类型) 意味着则该函数指针指向一个 返回类型是,参数是的函数
  3. 函数指针定义

int(*p)(int,int)=Add;

4.函数指针调用

(*p)(3,5);
p(3,5);
(*p)();
p();//不传参数

5.函数指针声明

void (* signal(int,int))(int);

6.函数指针类型和函数指针变量的写法

int(*p)(int,int);
void(*p)(int);

函数指针数组定义

char * arr[5]字符指针数组——数组——存放的是字符指针


int * arr[5]整形指针数组——数组——存放的是整形指针


int(*p[5])(int,int)   void(*p[5])(int,char)等等


函数指针数组——数组——存放的是函数指针(函数的地址)

把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10]])();
int *parr2[10]();
int (*)() parr3[10];

答案是:parr1 parr1 先和 [] 结合,说明parr1是数组。

              数组的内容是什么呢? 是 int (*)() 类型的函数指针。

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int Sub(int x, int y)
{
  return x - y;
}
int main()
{
  int(*p1)(int, int) = &Add;
  int(*p1)(int, int) = &Sub;
  //函数指针数组中存放的类型相同的多个元素
  int(*p[])(int, int) = { &Add,&Sub };
  //p是函数指针数组_存放函数指针的数组
  //均是返回类型为int,函数参数是int,int类型
}

函数指针数组的使用

函数指针数组的用途:转移表

使用条件:函数类型/函数参数类型必须一摸一样

#define _CRT_SECURE_NO_WARNINGS 1
//计算器
#include<stdio.h>
void meau()
{
  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;
  int y = 0;
  int ret = 0;
  do
  {
    meau();
    printf("请选择>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Add(x, y);
      printf("ret=%d\n", ret);
      break;
    case 2:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Sub(x, y);
      printf("ret=%d\n", ret);
      break;
    case 3:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Mul(x, y);
      printf("ret=%d\n", ret);
      break;
    case 4:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Div(x, y);
      printf("ret=%d\n", ret);
      break;
    case 0:
      printf("退出游戏");
      break;
    default:
      printf("选择错误,重新选择\n");
      break;
    }
  } while (input);
  return 0;
}

能不能让代码变得简单一点?

//简化后
#include<stdio.h>
void meau()
{
  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;
  int y = 0;
  int ret = 0;
  //用函数指针数组
  //
  do
  {
    meau();
    printf("请选择>\n");
    scanf("%d", &input);
    int(*p[])(int, int) = { NULL,&Add,&Sub,&Mul,&Div };
                          //0      1    2     3    4
    if (input == 0)
      printf("退出游戏\n");
    else if (input > 0 && input <= 4)
    {
      printf("请输入两个操作数:\n");
      scanf("%d %d", &x, &y);
      ret = (*p[input])(x, y);
      printf("ret=%d\n", ret);
      //  ret = p[input](x, y);//用函数指针数组的下标找到指向的函数
    }
    else//其他
      printf("选择错误,请重新选择!\n");
  } while (input);
  return 0;
}

当然出了使用函数指针数组去更高效的使用计算器,下章我们将使用回调函数去高效优化计算器!

指向函数指针数组的指针

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

指向整型指针数组的指针

#include<stdio.h>
int main()
{
  int a = 0;
  int b = 0;
  int c = 0;
  int* arr[] = { &a,&b,&c };//整形指针数组
  int* (*p)[3] = &arr;//p是指针,是指向整形指针数组的指针
  return 0;
}

函数指针数组的指针

#include<stdio.h>
int main()
{
  int(*arr[5])(int, int) = { NULL,&Add,&Sub,&Mul,&Div };
  p = &arr;//存放函数指针数组的指针
  int(*(*p)[5])(int, int) = &arr;
  return 0;
}

书写

无论是函数指针&函数指针数组&函数指针数组的指针等等,可以一直延申下去!

我们在书写变量是,首先将变量p写出来,在添加其类型。

也可以从简单的函数指针的基础上修改!

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!最近的心情多做事少说话。

代码------→【gitee:https://gitee.com/TSQXG

联系------→【邮箱:2784139418@qq.com】

目录
相关文章
|
7月前
|
存储 人工智能 Java
一文轻松拿捏C语言的指针的基础使用
本文介绍了C语言中的指针概念,包括直接访问和间接访问内存的方式、指针变量的定义与使用、取址运算符`&`和取值运算符`*`的应用,帮助读者深入理解指针这一C语言的核心概念。君志所向,一往无前!
136 0
|
9月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
299 7
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
9月前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
1066 9
|
9月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
258 7
|
10月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
10月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
10月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
10月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
588 3
|
10月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
10月前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
195 1