彻底搞定C指针-函数名与函数指针

函数名&函数名取地址

函数指针

通常我们可以将指针指向某类型的变量,称为类型指针(如,整型指针)。若将一个指针指向函数,则称为函数指针。

函数名的意义

函数名代表函数的入口地址,同样的,我们可以通过根据该地址进行函数调用,而非直接调用函数名。

 void test001(){
printf("hello, world");
} int main(){ printf("函数入口地址:%d", test001);//qt中的函数入口地址不会变,C中会变,这里仅为了说明问题
//test001();
int *testADD = (int *);//将地址转化为int型指针
void(*myfunc)() = testADD;//将函数写成函数指针,有些书上会写&testADD
myfunc(); //调用函数指针
system("pause");
return ;
}

另外,还有以下结论:

(1)test001的函数名与myfunc函数指针都是一样的,即都是函数指针。test001函数名是一个函数指针常量,而myfunc是一个函数指针变量,这是它们的关系。

 int test001(int a, char b){
printf("hello, world\n");
return ;
} int main(){ int(*myFun)(int, char) = test001;
myFun = test001; //下面四种表达式的结果是相同的
int a = ;
char b = 's';
myFun(a, b);
(*myFun)(a, b);
test001(a, b);
(*test001)(a, b); system("pause");
return ;
}

(2)testADD和&testADD的值一样,但表达的含义不同,与数组名和“&数组名”类似,详见指针和数组的关系

定义函数指针

定义函数指针最简单的是直接定义函数指针变量,另外还有定义函数类型和定义函数指针类型。

 int test001(int a, char b){
printf("hello, world\n");
return ;
} void test002(){ //定义函数类型
typedef int(Fun)(int, char);
Fun *funFir = test001; //定义函数指针类型
typedef int(*FunP)(int, char);
FunP funSec = test001; //定义函数指针变量
int(*funThi)(int, char) = NULL;//若报错,在强制转型,(int(*)(int , char))NULL
funThi = test001;
}

函数指针用于形参

这种用法通常出现在回调函数中,一般回调函数用于定制操作,下面的例子将说明如何进行定制操作

 /*
-----------------------
函数指针用作另一个函数的参数
-----------------------
*/
int con1(int a, int b){
return a + b;
} int con2(int a, int b){
return a - b;
} int con3(int a, int b){
return a + b + ;
} //在函数体中显式调用函数,将失去灵活性
//尽管我可以用switch实现三种con的切换
void doc(){
int a = ;
int b = ;
int ret = con1(a, b);
} //用如下的调用方式,调用者并不知道调用的哪个函数
//因此根据函数指针的函数原型可以自己实现新函数,并进行调用
int doc_p(int(*temp)(int ,char)){
int a = ;
int b = ;
int ret = temp(a,b);
return ret;
} /*
---------------------
函数指针数组
---------------------
*/
void func1(){
printf("a");
}
void func2(){
printf("a");
}
void func3(){
printf("a");
} void test003(){
int(*func[])();
func[] = func1;
func[] = func2;
func[] = func3; for (int i = ; i < ; ++i)
{
func[i];
}
}

为什么我们要把函数作为参数来调用呢,直接在函数体里面调用不好吗?

在这个意义上,“把函数做成参数”和“把变量做成参数”目的是一致的,就是以不变应万变。形参是不变的,而实参是可以定制的。唯一不同的是,普通的实参可以由计算机程序自动产生,而函数这种参数计算机程序是无法自己写出来的,因为函数本身就是程序,它必须由人来写。所以对于回调函数这种参数而言,它的“变”在于人有变或者人的需求有变。

回调函数

回调函数和普通函数完成的功能是一样的,但回调函数更灵活,普通函数在函数体中调用,失去了变量的灵活性,有点类似于模板编程。

(1)首先是通过内存偏移,访问数组的各元素地址,两种方法的结果相同

 void printAll(void *arr, int eleSize, int len){

     char *start = (char*)arr; //强制转型
for (int i = ; i < len; ++i){
printf("%d\n", start+i*eleSize);//内存偏移
}
} void test004(){
int arr[] = {,,,,};
printAll(arr, sizeof(int), );
printf("-------------------\n");
for (int i = ; i < ; ++i){
printf("%d\n", &arr[i]);
}
} int main(){
test004(); system("pause");
return ;
}

(2)对上面的函数用函数指针进行改写

 //添加函数指针作形参,必须写明变量名
void printAll(void *arr, int eleSize, int len, void(*print)(void *data)){ char *start = (char*)arr; //强制转型
for (int i = ; i < len; ++i){
char *eleAddr = start + i*eleSize;
print(eleAddr);
//print(start+i*eleSize);
}
} //自定义的被调用函数
void Myprint(void * data){
int *p = (int *)data;
printf("%d\n", *p);
} void test004(){
int arr[] = {,,,,};
printAll(arr, sizeof(int), , Myprint);
} int main(){
test004(); system("pause");
return ;
}

(3)对上面的函数指针添加自定义的数据类型

 //添加函数指针作形参,必须写明变量名
void printAll(void *arr, int eleSize, int len, void(*print)(void *data)){ char *start = (char*)arr; //强制转型
for (int i = ; i < len; ++i){
char *eleAddr = start + i*eleSize;
print(eleAddr);
//print(start+i*eleSize);
}
} //自定义的被调用函数
void Myprint(void * data){
int *p = (int *)data;
printf("%d\n", *p);
} struct Person{
char name[];
int age;
}; //添加自定义的数据类型打印函数
void MyprintStruct(void * data){
struct Person *p = (struct Person *)data;
printf("%s,%d\n", p->name, p->age);
} void test004(){
int arr[] = {,,,,};
printAll(arr, sizeof(int), , Myprint); struct Person person[] = {
{"aaa", },
{"bbb", }
};
printAll(person, sizeof(struct Person), , MyprintStruct); } int main(){
test004(); system("pause");
return ;
}

回调函数最大的优势在于灵活操作,可以实现用户定制的函数,降低耦合性,实现多样性。

C语言函数指针和回调函数的更多相关文章

  1. VC++的函数指针和回调函数 及友元函数

    什么是函数指针 函数指针是指向函数的指针变量.也就是说,它是一个指针变量,而且该指针指向一个函数. 对于指针变量来说,它的值是它指向的变量的地址.举个例子:指针变量pi是指向一个整型变量i的指针,则变 ...

  2. 深入浅出剖析C语言函数指针与回调函数(一)【转】

    本文转载自:http://blog.csdn.net/morixinguan/article/details/65494239 关于静态库和动态库的使用和制作方法. http://blog.csdn. ...

  3. C语言之函数指针、回调函数的使用

    一.背景 首先看下如下代码,这个定义是放在头文件的,在程序中tCdrvCallbackFkt也定义了另一个变量,而且括号后面还跟定义了几个变量,不理解这个定义. typedef void (PUBLI ...

  4. C 函数指针、回调函数

    参考链接:https://www.runoob.com/cprogramming/c-fun-pointer-callback.html 函数指针 函数指针就是执行函数的指针,他可以像正常函数一样去调 ...

  5. c中函数指针和回调函数

    函数指针: 指向函数的指针.(定义的函数会分配一块内存,同变量一样存在首地址)示例如下: int Func(int x); /*声明一个函数*/ int (*p) (int x); /*定义一个函数指 ...

  6. C 函数指针与回调函数

    函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: #inclu ...

  7. 声明函数指针、回调函数、函数对象------c++程序设计基础、编程抽象与算法策略

    声明函数指针 #include<iostream> using namespace std; double a(double aa) { return aa; } int main() { ...

  8. 【不在混淆的C】指针函数、函数指针、回调函数

    一.指针函数 函数的返回值是指针类型. int* fun(int a,int b); 指针函数使用: 返回字符串 这里要注意,"1234567890abc"是字符串常量,*p指向的 ...

  9. C:函数指针、回调函数

    函数指针 是一个指针,指向函数的指针,指针存放的都是地址,所以函数指针存放的是函数的地址.数组名就是数组的首地址,函数名就是函数的首地址.与数组类似. 代码demo int (*p) (int ,in ...

随机推荐

  1. Linux tee命令使用详解分享

    tee命令主要被用来向standout(标准输出流,通常是命令执行窗口)输出的同时也将内容输出到文件,下面是tee的man 信息 read from standard input and write ...

  2. thinkphp 字段定义

    通常每个模型类是操作某个数据表,在大多数情况下,系统会自动获取当前数据表的字段信息. 系统会在模型首次实例化的时候自动获取数据表的字段信息(而且只需要一次,以后会永久缓存字段信息,除非设置不缓存或者删 ...

  3. Heartbeat基本介绍----HA / vmware HA FT

    Heartbeat是High-Availability Linux Project (Linux下的高可用性项目)的产物,是一套提供防止业务主机因不可避免的意外性或计划性宕机问题的高可用性软件.Hea ...

  4. Vue-Grid-Layout分享一款好用的可拖拽组件

    在使用Grafana的过程中,发现Grafana关于视图页面中每一个面板都可拖拽,可随意放大放小,体验非常棒,F12看了Grafana的代码,看打包后的代码很像react,进一步css,看到有grid ...

  5. typeerror: __init__() missing 2 required positional arguments: 'inputs' and 'outputs'

    1 问题描述 使用下边这条命令去检查 TensorFlow Object Detection API是否正确安装: python object_detection\builders\model_bui ...

  6. 解决在Spring整合Hibernate配置tx事务管理器出现错误的问题

    问题描述: Error occured processing XML 'org/aopalliance/intercept/MethodInterceptor'. See Error Log for ...

  7. printk函数速率限制

    如果你不小心, 你会发现自己用 printk 产生了上千条消息, 压倒了控制台并且, 可能地, 使系统日志文件溢出. 当使用一个慢速控制台设备(例如, 一个串口), 过量的消息速率也 能拖慢系统或者只 ...

  8. Ascii码 、16进制与 char

            对于一个非计算机专业出身的人,以前只知道计算机中所有的数据都是以二进制形式进行存储,计算,通信的.但是人类文明中,主要的信息展现以文本的形式展现的.如果使用内存中的0和1来表示文本一直 ...

  9. 继承Activity和View

    1,当你自定一个继承自view的视图A之后, 如果你在一个继承自Activity的组件B中需要使用A里面的一些方法,如果在B中需要使用A中的一些方法好像不可以直接使用. 需要在B中使用setConte ...

  10. order方法属于模型的连贯操作方法之一

    order方法属于模型的连贯操作方法之一,用于对操作的结果排序. 用法如下: $Model->where('status=1')->order('id desc')->limit(5 ...