当前位置:首页 > 公众号精选 > C语言与CPP编程
[导读]关于指针,前面文章C语言指针详解有过介绍,这里主要讨论函数指针和指针函数。 1 什么是指针? 定义:指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量; 上面一个 4GB 的内存可以存放 2^32 字节的数据。左侧连续的十六进制编号就是内存地址

关于指针,前面文章C语言指针详解有过介绍,这里主要讨论函数指针和指针函数。

1 什么是指针?

定义:指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量;


上面一个 4GB的内存可以存放 2^32字节的数据。左侧连续的十六进制编号就是内存地址,每个内存地址对应一个字节的内存空间。而指针变量保存的就是这个编号,也即内存地址。

指针的声明:

指针其实就是一个变量,指针的声明方式与一般的变量声明类似,如下:

int *p;         // 声明一个 int 类型的指针 p,该指针指向一个int类型的对象
char *p         // 声明一个 char 类型的指针 p,该指针指向一个int类型的对象
int *arr[10]    // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向 int 类型对象的指针
int (*arr)[10]  // 声明一个数组指针,该指针指向一个 int 类型的一维数组
int **p;        // 声明一个指针 p ,该指针指向一个 int 类型的指针

声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问之前,指针必须进行初始化:或是使他指向现有的内存,或者给他动态分配内存,否则我们并不知道指针指向哪儿,这个问题需要特别关注。

2 什么是函数指针?

函数指针定义:函数指针是指向函数的指针变量。因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。

其通用表达式为:类型说明符 (*函数名) (参数)

int (*fun)(int x)  //函数指针的定义
int (*fun)(int x,int y) //函数指针的定义

函数指针在PC软件开发中使用较少,在嵌入式行业使用较多,但是无论是PC软件还是嵌入式软件,理解函数指针的定义和使用,对于理解程序设计都是很有好处的。

函数指针的赋值

函数指针和其他指针一样定义之后使用之前也是需要初始化。

函数指针有两个用途:调用函数和做函数的参数

int (*fun)(int x,int y) //函数指针的定义
fun = &Function          //函数指针的赋值方式1
fun = Function           //函数指针的赋值方式2
x = (*fun)()             //函数指针的调用方式1
x = fun()                //函数指针的调用方式2

函数赋值的时候取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,并且赋值的时候函数不需要带圆括号;

如果是函数调用,还必须包含一个圆括号括起来的参数表。

函数指针的用法

我们使用指针的时候,需要通过钥匙 *来取其指向的内存里面的值,函数指针使用也如此。通过用(*pf)取出存在这个地址上的函数,然后调用它。

char*  fun(char* p1,char* p2)
{
  int i = 0;
  i = strcmp(p1,p2); if(0 == i)
  { return p1;
  } else { return p2;
  }
}
int main()
{
  char * (*pf)(char* p1,char* p2);
  pf = &fun;
  (*pf)("aa","bb"); return 0;
}

这里需要注意到是,在Visual C++6.0里,给函数指针赋值时,可以用&fun或直接用函数名fun。这是因为函数名被编译之后其实就是一个地址,所以这里两种用法没有本质的差别。

用法延申

当我们不满足于函数指针上面如此简单的用法时,这时候需要一个高级用法来扩展我们对于函数指针的认知边界。

感兴趣的同学可以看看下面这个用法,并尝试理解该表达式是如何使用的函数指针。

(* (void(*)()) 0)(); //出自《C Trap and Pitfalls》这本经典的书

答案如下:   ``

  • 第一步:通过 void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
  • 第二步:通过 (void(*) ())0,可以明白这是将 0强制转换为函数指针类型, 0是一个地址,也就是说一个函数存在首地址为 0的一段区域内。
  • 第三步:通过 (*(void(*) ())0),可以明白这是取0地址开始的一段内存里面的内容。
  • 第四步:最终理解 (*(void(*) ())0)(),这是函数调用。

让程序跳转到绝对地址为0x0113F90C

方法一:

  • 0x0113F90C地址强制转换为函数指针类型,即: (void (*)())0x0113F90C
  • 然后调用: ((void (*)())0x0113F90C)()

方法二:

typedef (void (*)())  VoidFuncPtr;
((VoidFuncPtr)0x0113F90C)();

面试题:指出程序的错误

#include void main(void)
{
   int max(x,y);
   int *p=max;
   int a,b,c,d;
   scanf("%d %d %d",a,b,c);
   d=p(p(a,b),c); printf("d:%d\n",d); return;
}
int max(int a,int y)
{ return(x > y ?x:y);
}

答案

  • int max(x ,y);函数声明错误,改为: int max(int x,int y);

解析:max函数声明只是写出了函数的形参的名称,这对参数的类型来说是毫无意义的,编译器会把x和y当做数据类型来看,编译时会出错,max的调用肯定也会出错。

  • int *p=max;指针定义错误,改为: int (*p)(int ,int)=max;

解析:函数的指针是不能直接赋值给int型指针.

  • scanf("%d %d %d",a,b,c);库函数使用错误,改为 scanf("%d %d%d",&a,&b,&c);

解析:库函数使用错误,第二部分应该是接收数据的地址,这里却写成了变量。

  • d=p(p(a,b),c);函数指针调用函数错误,改为 d=(*p)((*p)(a,b),c);`

解析:用函数指针调用函数的格式如下:(【*】【函数指针名称】)(【参数列表】);不能直接用函数指针加上参数就直接调用

3 什么是指针函数?

指针函数定义:指针函数的落脚点是一个函数,这个函数的返回值是一个指针,与普通函数int function(int,int)类似,只是返回的数据类型不一样而已。

_type_ *function(int, int) //返回的是指针地址
int function(int,int)     //返回的是int型数据。
int  *fun(int x)        //指针函数的定义
int * fun(int x,int y)  //指针函数的定义
int* fun(int x,int y)   //指针函数的定义

以上三种写法均正确,但是*靠近返回值一点更容易理解。

指针函数的调用

在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。

typedef struct _Data{
    int a;
    int b;
}Data;

//指针函数
Data* f(int a,int b){
    Data * data = new Data;
    data->a = a;
    data->b = b; return data;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //调用指针函数
    Data * myData = f(4,5);
    qDebug() << "f(4,5) = " << myData->a << myData->b; return a.exec();
}

不过也可以将其返回值定义为 void*类型,在调用的时候强制转换返回值为自己想要的类型。

其输出结果是一样的,不过不建议这么使用,因为强制转换可能会带来风险。返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。

事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。

比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”。

4 函数指针与指针函数区别

通过以上的介绍,小伙伴应该都能理解二者的定义。那么简单的总结下二者的区别:

1. 定义不同

  • 指针函数本质是一个函数,其返回值为指针。
  • 函数指针本质是一个指针,其指向一个函数。

2. 写法不同

  • 指针函数: int* fun(int x,int y);
  • 函数指针: int (*fun)(int x,int y);

可以简单粗暴的理解为,指针函数的*是属于数据类型的,而函数指针的星号是属于函数名的。

再简单一点,可以这样辨别两者:函数名带括号的就是函数指针,否则就是指针函数。

3. 用法不同

上面函数指针和指针函数的用法都有,但是函数指针的用法会更多,相对而言难度也更大,例如函数指针与回调函数,如果是C++非静态成员函数指针,其用法也会有一些区别,感兴趣的同学可以关注后续推文或自行查阅相关书籍。

总而言之,这两个东西很容易搞混淆,一定要深入理解其两者定义和区别,避免犯错。


免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

Python由荷兰数学和计算机科学研究学会的吉多·范罗苏姆于1990年代初设计,作为一门叫做ABC语言的替代品。 Python提供了高效的高级数据结构,还能简单有效地面向对象编程。

关键字: python 函数 对象编程

测试数据综合分析的绝佳工具,深受工程师和研究员欢迎

关键字: 后处理分析软件 向导 函数

由上图中可以知道进程地址空间中最顶部的段是栈,代码中调用函数、定义局部变量(但不包含static修饰的变量)或声明的类的实例等等都要使用栈空间,当函数执行完(也就是程序执行超过了这个函数的作用范围的时候),操作系统会把该...

关键字: 进程地址 局部变量 函数

星标/置顶 公众号,硬核文章第一时间送达!链接| https://zhuanlan.zhihu.com/p/274473971题很多,先上题后上答案,便于大家思考问题点:1、C和C的特点与区别?2、C的多态3、虚函数实现...

关键字: 腾讯 函数 进程 AI

程序接口是操作系统为用户提供的两类接口之一,编程人员在程序中通过程序接口来请求操作系统提供服务。面向过程语言最基本的单元是过程和函数。

关键字: 程序接口 过程 函数

星标「嵌入式大杂烩」,一起进步!链接:https://www.cnblogs.com/jozochen/p/8541714.html一、问题复现稳定复现问题才能正确的对问题进行定位、解决以及验证。一般来说,越容易复现的问...

关键字: 嵌入式开发 函数 代码 寄存器

基本上,没有人会将大段的C语言代码全部塞入main()函数。更好的做法是按照复用率高、耦合性低的原则,尽可能的将代码拆分不同的功能模块,并封装成函数。C语言代码的组合千变万化,因此函数的功能可能会比较复杂,不同的输入,常...

关键字: 函数 PEN C语言代码 C语言程序

Part1一、让自己习惯C条款01:视C为一个语言联邦C并不是一个带有一组守则的一体语言:他是从四个次语言(C、Object-OrientedC、Template、STL) 组成的联邦政府,每个次语言都有自己的规约。记住...

关键字: 函数 ASPECT 编译器

为什么会写篇栈变化的文章?做系统分析的话你肯定遇到过一些crash,oops等棘手问题,一般大家都会用gdb,objdump或者addr2line等工具分析pc位置来定位出错的地方。但是这些分析工具背后的本质原理就不见得...

关键字: 函数 ARM C语言 AI

前言:一转眼从事前端已经6年了,从当时的小白到如今大厂的技术专家,中间也走过不少弯路,从今天开始我会持续更新前端技术文章,并且整体的文章会进行体系梳理,整个知识体系分为:基础精讲,框架讲解,框架及工具原理,前端面试题精讲...

关键字: 函数 GE FUNCTION APP
关闭
关闭