C语言中的函数指针学习笔记

所属分类: 软件编程 / C 语言 阅读数: 179
收藏 0 赞 0 分享

一、定义函数指针

return_type (*func_pointer)(parameter_list)

普通指针变量的定义

int * p;
char * pointer;

类型的限定都在变量前面;
函数指针类型的限定是前后都有,前面是返回类型,后面是输入参数。

2016420154232853.png (449×127)

利用typedef 可以简化上面的表达方式。

typedef return_type (*FunctionPointer) (parameter_list);
FunctionPointer func_pointer;

这样是不是容易读了,和上面的功能一样,定义了一个返回类型为return_type ,输入参数为parameter_list的函数指针。

二、定义返回函数指针的函数
return_type(*function(func_parameter_list))(parameter_list)

2016420154256143.png (558×129)

方框圈出来的表示返回类型为函数指针,剩下的部分就表示一个function函数,输入参数为func_parameter_list。
它就等价于 FunctionPointer function(func_parameter_list); 。
再看看:

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

2016420154323228.png (400×63)

signal是一个返回函数指针的函数,signal的输入为int 变量和一个函数指针。

三、函数指针的使用

#include <stdio.h> 
int add(int a, int b); 
void main() 
{ 
  int(*fun1)(int a, int b) = add; 
  int(*fun2)(int a, int b) = &add; 
  int(*fun3)(int a, int b) = *add; 

  printf("%d\n", fun1(1, 2)); 
  printf("%d\n", fun2(1, 2)); 
  printf("%d\n", fun3(1, 2)); 

  char input[10]; 
  gets(input); 
} 
int add(int a, int b) 
{ 
  return a + b; 
}

函数名会被隐式的转变为指针,前面加*和&操作符都不起作用,printf的结果都是3。

四、神奇的代码

int (*(*pf())())()
{ return nullptr; }

哇哦,这是个什么函数!画个框框分解它

2016420154405912.png (400×63)

小框表示返回的是一个函数指针,在圈个大框,又是一个函数指针。
它就表示,pf() 返回的是一个函数指针,这个函数指针对应一个无输入参数的函数:返回值也是函数指针(对应无输入参数的函数,返回值为int类型)。好复杂啊,有点晕!
利用typedef 简化一下。

typedef int(*Fun1) (); 
typedef Fun1(*Fun2) (); 
Fun2 pf() 
{ 
   return nullptr; 
}

这样看就舒服多了。

五、这又是什么鬼!

(*(void(*) ())0)();

画个框看看:

2016420154437319.png (299×61)

小框里代表一个函数指针,常数前面加括号代表类型的强制转换。咦,它把0强制转换成了一个函数指针,并执行!这是什么操作啊!
六、一段验证代码

#include <stdio.h> 
typedef int Function(int, int); 
typedef int(*FunctionPointer1) (int, int); 
typedef FunctionPointer1(*FunctionPointer2) (); 
int fun1(int a, int b) 
{ 
  return a + b; 
} 

FunctionPointer1 fun2() 
{ 
  return fun1; 
} 
FunctionPointer2 fun3() 
{ 
  return fun2; 
} 
int(*(*fun4())())(int, int) 
{ 
  return fun2; 
} 

void main() 
{ 
  Function* fuction = fun1; 
  FunctionPointer1 fun = fun1; 
  int a = fun3()()(3, 4); 
  int b = fun4()()(5, 6); 
  printf("%d\n%d\n", a, b); 
  printf("fun1:%d\n*fun1:%d\n&fun1:%d", fun1, *fun1, &fun1); 
  printf("fun:%d\n*fun:%d\n&fun:%d", fun, *fun, &fun); 
  char chars[10]; 
  gets(chars); 
}

函数名前面加不加*,&操作符,都是一个效果;函数指针前面加不加*操作符是一个效果,但是加上&操作符就代表着取指针的地址了。
可以通过typedef int Function(int, int); 为一种类型的函数定义别名,但是使用的时候只能定义指针形式的变量:

Function* fuction = fun1;

七、一个问题
在stackoverflow上偶尔看到如下的问题,代码如下

#include
void hello() { printf("hello"); }
int hello_1()
{
    printf("hello 1");
    return 0;
}
 
int main(void) {
  (*****hello)();
  (****hello_1)();
}

   执行结果是无论hello前面有多少个指针符号,都是执行hello()函数,打印“hello”。
  为什么出现这样的结果呢:
    用指针指向一个函数是OK的,但是仍然还要被转化为一个function pointer。其实使用*来指向一个函数 == CALL这个函数。因此无论指向多少次,仍然也是调用这个函数。
为什么一个函数会被转化成一个指针呢?答案就是将函数默认的转换成函数指针,可以减少&的使用,编译器默认的将函数转化为函数指针,也省得你每次调用函数时加*调用函数。
  哈哈,也就是我们之前说的,函数即指针。似乎有点不是很清晰,再看下面的例子

void foo() {
    printf("Foo to you too!...\n");
};
 
int a = 2;
int* test()
{
    return &a;
}
int main()
{
  int i;
  void (*p1_foo)() = foo;
  void (*p2_foo)() = *foo;
  void (*p3_foo)() = &foo;
  void (*p4_foo)() = *&foo;
  void (*p5_foo)() = &*foo;
  void (*p6_foo)() = **foo;
  void (*p7_foo)() = **********************foo;
 
  (*p1_foo)();
  (*p2_foo)();
  (*p3_foo)();
  (*p4_foo)();
  (*p5_foo)();
  (*p6_foo)();
  (*p7_foo)();
  i = *(***test)();
printf("i=%d\n",i);
}

上面的列子不出例外,都能正常打印我们想要的数据。
但是对于&,则要进行仔细的分析一下:
&对于一个函数的操作,是返回一个指针,指向函数的指针,如果在对此指针执行&也就是&&foo,则会返回error,因为&foo是一个指针数值,也就是一个rvalue类型,再对他进行&操作,显然是返回error的。

&&foo  //EROOR
&*&*&*&*&*&*foo //OK
&******&foo  //OK

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多