你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

一个有意思的题目——函数指针,指针函数,函数指针数组

[复制链接]
gaosmile 发布时间:2020-9-12 14:59

这是一个群友发的笔试题目,里面涉及的东西也比较有意思。

直接看代码

void (*f[])(char *)' a' K* e: t: R5 n

这个是个什么东西?

我们先看看下面的东西

函数指针和指针函数的定义

我们看个代码

int *func(int a,int b)
$ t) `# X/ U7 |1 ?5 k4 o5 \

我们之前说过运算符的优先级,「 * 」  的优先级低于「(  」

由于「 * 」的优先级低于「 () 」的优先级,因而func首先和后面的「 () 」结合,也就意味着,func是一个函数。即:

int *(func)(int a,int b)# ?8 B2 ?; p/ J0 f) M

然后这个函数的返回值的类型是「  int * 」 即:指向int类型的指针

然后我们这样修改上面的代码

int (*func)(int a,int b)6 J  S8 i  K* D, q3 s' b# s

「(*func) 」说明func是一个指针,然后后面跟着「()」说明这个指针指向一个函数,即指向函数的指针。

所以

函数指针: 首先是一个指针,这个指针指向一个函数

指针函数:首先是一个函数,这个函数的返回值一个指针9 {2 Z" J$ `  S$ K. D  @: Y

用typedef声明一个函数指针

我们声明一个函数指针,正常方法是

int (*pfunc)(int a,int b)
0 y9 Z: t! Z& T

当我们命名很多个函数指针的时候,用上面的方法显得非常不方便,所以我们可以这样做

typedef int (*PF) (int a,intb)9 }8 o, A9 L+ v' ~( r3 c$ n
PF pfunc;: P9 l* b+ ?6 H& ], r4 O

例程:

#include "stdio.h"4 ?, g+ F; I0 s8 Z6 M  `

6 k$ M1 S, h' l% R& Q5 j* ktypedef int(*PF)(int, int);3 [( p5 T2 P$ A: m/ S+ p
$ @! @. T3 K9 a6 m6 Y- B9 \
int add(int a, int b)( C  N) _# T2 y" q- ]
{
* {7 \; R6 z. ^* U0 { return a + b;3 h  Z  E: }8 b% `! V
}
1 `" Q+ x8 K4 [( x1 m
1 q* Q  Y# ]+ d$ |" F; Kint reduce(int a, int b)- g2 S' P4 y$ s# O: n# X
{1 X9 W8 j/ l* Y& x7 m
return a - b;* o0 b; ]! q4 |3 B/ R  G; e
}2 B& N: V. ]7 |2 ^4 Z
* ^# F& A4 _5 r& W; x/ u
int main()
: U/ d) W3 |1 c/ a  z5 n4 K{
: U( ^6 m+ V8 V1 D5 x6 y* y5 a( | PF pfunc = NULL;& l1 Y* U) ?! O( H, T' S& `
pfunc = add;9 Z; u! |7 }: G/ |# c
printf("add:%d\n",pfunc(3, 4));+ P3 ~  u/ Z( \6 U
pfunc = reduce;/ h+ x' A! V/ T- y0 x
printf("reduce:%d\n", pfunc(3, 4));
9 d, H) m9 n" M4 @! F /*getchar用VS编写方便查看输出*/
4 \/ F" ]2 S" w5 r% q1 K( _ getchar();
7 r1 [: ~9 t% D3 i; [ return 0;; ]2 v7 k  C6 U6 `7 K6 o
}* g- @4 v# S* F( ?9 T1 L" `
微信图片_20200912145623.png img再说回上面的那个题目void (*f[])(char *)" B0 ^3 z& Y8 x' ]7 }  Q9 N

f 是个什么鬼东西?

[] 的优先级 比 *的优先级高,所以 f首先是修饰了数组,然后跟后面的 *组合,就说明这个数组里面住的都是 指针,这些指针是什么呢,再出来看看就看到了,这个指针是 一个函数,这个函数的 参数是 char *返回值是void。

示例代码

#include <stdio.h>9 b  [+ z1 m4 L- ~  y  C1 y, R( V# B

: Q7 {- o- ?! O: K7 Y8 y9 Z  i" {: `+ Gvoid (*f[3])(char *);; Z8 ?2 ~: I0 s4 X$ P( o

+ T  F! A: w4 q& V/ H4 ~! B1 }void efunction(char * s)4 u& W/ g) b! r/ ~
{
# M9 {3 c) z* ?9 |% j" }2 \9 U printf("%s\n",s);
0 N" S- ?0 J8 \; H; _7 C}( v7 H$ L* a9 C

+ a  V0 n2 l8 m8 Mint main()
, d9 O$ V6 O. b' Z2 b, D" c{
- l4 n0 `4 p  r+ y' i3 ^& x1 @ f[0] = efunction;
  n% Z/ x0 J: ]1 t! X* V //void (*f[])(char *) = {efunction}; , D, P8 w3 _, ?5 {4 A6 B
(*f[0])("hello code");
5 M* l8 Q& h( a0 }5 F; q9 x& b7 q    return 0;
8 V% P" O" p" h}% Z2 w8 ~" @% B

代码输出

hello code
1 |" P" H0 B9 n/ W& G; p- _+ t4 P: \- |  C4 \+ O6 s$ `( e* i0 t
--------------------------------
7 s. a- `) p2 q1 V) sProcess exited after 0.08441 seconds with return value 0  P# P6 A* H! t* m- }
请按任意键继续. . .1 X) j: |; J) y" m% ]* G, K  Z
函数指针在项目里的实际应用

这是我的android项目hal部分的代码,这部分代码用到的是函数指针,通过name来调用不同的函数。

微信图片_20200912145627.png
android hal部分代码懂了这些,我们就可以看懂别人的代码了

我们有时候看别人的代码时候,经常是一面懵逼,比如下面这个void (*p)();还有这个(*(void(*) ())0)();

我记得我在以前的文章里面有谈到一个右左原则,从p开始看,往右走直到遇到)再往左走遇到(,(*p)我们就可以看出p是一个指针,继续分析往右走,遇到(),说明p指向一个(void)的函数,往左走,知道p指向的函数返回值是void。

ok,看下面的例子。

#include "stdio.h"1 ]5 l8 S4 C& O
1 t) A5 G  Q9 v( c! X* E" D6 ]7 O
void Function()
/ Y2 s& P: ^9 f2 X2 _6 B{
  a4 A- R) Q$ I$ g0 w+ K    printf("Call    Function!\n");. j7 w' z$ x& ~4 ^$ ^8 G
}
* V0 v4 f/ z; c& p  x" d  j
4 q# ?  }4 d  w! b# oint main(). n) S  e6 t6 J" p* N
{
5 U' l8 Y8 _8 R2 t    void(*p)();! U3 Q2 @/ T" k
    *(int*)&p = (int)Function;! q3 r5 w" @; o* ~
    (*p)();. O& N0 {  \& ^0 z# m0 x( @' }; V* R
    getchar();
" P1 c: ]) A) q: _/ T6 r$ S% A8 h3 o    return 0;
1 ^2 m0 z2 b7 g: G}% [$ l9 e0 U4 l, U8 A
微信图片_20200912145631.png 输出结果

然后继续分析(*(void(*) ())0)();

  • 1、void (*)()  我们上面分析了这个是一个函数指针,只是把p去掉了而已。
  • 2、把上面的void (*)()用PN代替,上面的表达式变成(*(PN)0)()N后面有一个0,这个是让我们咋舌的地方,然后我们看一下 (char)a;这样的表达式,所以*(PN)0就是把0当成一个地址,强制转换为PN类型,用*这个钥匙取出这个地址区域的值。
  • 3、把(*(PN)0)()替换成PM,原来的表达式变成PM(),这样大家看起来比较容易了吧,就是正常的函数调用。( c, E5 N3 ?" T3 R! i6 C
给个例子自己去参透一下#include <stdio.h>- ~9 P9 V# l% U1 V' P. O8 m0 c1 p  {
#include <string.h>0 q: D. [2 g3 x! v
( N1 \9 K1 d5 F* P2 Y( o7 J
char * fun1(char * p)( a; G8 w' }* S( _# o! R
{
2 G5 Z. B. Y  |" v( r" S6 D6 o" N    printf("%s\n", p);
; V  T; N2 J) A0 p) Q2 g    return p;1 P$ i0 ^' D- h! d- d2 W4 ^1 V
}
+ T9 c' [* z& {8 e& ^5 j/ w4 n6 u
char * fun2(char * p)
; _) ^  W4 A0 C{
' Y, Q, T& \# I- j: z8 T0 m# l: o    printf("%s\n", p);
( U0 W5 g6 H( G2 u( T+ Z    return p;6 J2 |8 N0 V- w+ @) r
}
6 H5 _: Y/ L1 C; b4 D9 _4 m2 b- x" e7 m' W
char * fun3(char * p)
) t% e/ k) _2 {* e* W1 D( q{
' I, M5 K& W1 D$ Z* n    printf("%s\n", p);0 P9 Z) W0 Q" _0 ]0 G9 N# Q
    return p;2 w3 @/ C+ v4 [  n
}  o5 s) M( {* K; i% `7 X7 q" m
4 @; |# O0 C, O8 ^( n* K
int main()
' r3 j0 o8 M$ H7 ^# [0 K9 n; k{
" [  @( n( _/ x  _    char * (*pf[3])(char * p);
9 {) h8 @/ [, L" ?, R" x+ S    pf[0] = fun1; // 可以直接用函数名
5 Q  v0 g3 m, X+ r3 @, ^& z! X    pf[1] = &fun2; // 可以用函数名加上取地址符' c! O  b2 A1 v5 V6 S% d
    pf[2] = &fun3;/ d; `& }  G" E/ d2 n
7 _5 q' b( w9 t  a/ j" ?
    pf[0]("fun1");
1 f$ |/ Q- G0 y8 \    pf[0]("fun2");) k1 m% _) V2 z$ f! F
    pf[0]("fun3");: s4 b5 I; |0 U% o8 f

" n/ B; g9 F2 k2 {    getchar();# e! e6 m: X8 [( a$ c/ W
    return 0;
  p' v& {. L4 L}

' Q& k1 ~" _% Q% @) Q
赞 收藏 评论0 发布时间:2020-9-12 14:59

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版