彻底搞定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. make 基础

    Make这个词,英语的意思是"制作".Make命令直接用了这个意思,就是要做出某个文件.比如,要做出文件a.txt,就可以执行下面的命令. $ make a.txt 但是,如果你真 ...

  2. ForkJoin学习笔记

    1.Fork/Join框架:(分治算法思想) 在必要的情况下,将一个大任务,进行拆分(fork) 成若干个子任务(拆到不能再拆,这里就是指我们制定的拆分的临界值),再将一个个小任务的结果进行join汇 ...

  3. Apache Flink 进阶(三):Checkpoint 原理解析与应用实践

    大家好,今天我将跟大家分享一下 Flink 里面的 Checkpoint,共分为四个部分.首先讲一下 Checkpoint 与 state 的关系,然后介绍什么是 state,第三部分介绍如何在 Fl ...

  4. helm安装kubernetes的插件istio

    1.安装istio 要使用Helm自定义Istio安装,请使用--set <key>=<value>Helm命令中的选项覆盖一个或多个值 怎么使用选项配置请查看官网https: ...

  5. js单线程

    由于js是运行在单线程上的,所有浏览器单独开启一个线程来处理事件消息的轮询,避免阻塞js的执行.

  6. debezium监听数据库变化Date类型数据的还原

    debezium是一个开源的分布式CDC系统,支持对接各种数据源,将数据源中已持久化的数据变更捕获后写入消息队列. 当数据源是mysql时,debezium通过BINLOG实时捕获已提交事务数据. 在 ...

  7. (一)通过JAVA连接SAP (sapjco3.jar在Windows和MacOS上的配置)

    (一)通过JAVA连接SAP调用接口 (sapjco3.jar在Windows和MacOS上的配置) 一.sapjoc3.jar获取 由于sap官网提供的链接需要合作公司提供账号密码,如果商用请索要正 ...

  8. Spring的refresh()方法相关异常

    如果是经常使用Spring,特别有自己新建ApplicationContext对象的经历的人,肯定见过这么几条异常消息:1.LifecycleProcessor not initialized - c ...

  9. Luogu P1039 侦探推理(模拟+枚举)

    P1039 侦探推理 题意 题目描述 明明同学最近迷上了侦探漫画<柯南>并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏.游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯 ...

  10. JavaScript中对象的3种定义方式

    对象是有特性(属性)和功能(方法)的集合体. 定义对象有以下3种方式: 1.使用系统的new Object()方式定义对象 2.使用对象字面量定义对象( 即使用{}语法糖结构定义对象 ) 3.使用自定 ...