彻底搞定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. thinkphp ASSIGN标签

    ASSIGN标签用于在模板文件中赋值变量,用法如下: 直线电机厂家 <assign name="var" value="123" /> 在运行模板的 ...

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

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

  3. 现金贷平台下载量TOP100 涉逾30家P2P

    一.什么是现金贷,现状如何 那么什么是现金贷呢?在笔者看来,狭义的现金贷主要是指基于互联网等技术手段的小额现金贷款,广义的现金贷可以包括任何以小额现金和存款为标的进行借贷的行为,是一种无担保.无抵押. ...

  4. 框架:Flutter(移动应用程序开发框架)

    ylbtech-框架:Flutter(移动应用程序开发框架) Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作. ...

  5. ps快速将白底图片变为透明图片

    方法一: 如果图层有锁图标,则要点击它,然它消失.然后选中魔棒工具,然后点击图片上要透明的区域,按下backspace键即可. 方法二: 转载自:https://blog.csdn.net/sunyi ...

  6. 4_4.springboot之Web开发登录和拦截器

    1.登录处理 1).禁用模板引擎的缓存 # 禁用缓存 spring.thymeleaf.cache=false 2).页面修改完用ctrl+f9:重新编译: LoginController @Cont ...

  7. 用户管理模块之mysql.user

    不使用-h参数来指定登录host,默认会连接localhost,仅当mysql.user表中有一条对应的localhost访问授权(username@%不对任何主机做限制也不行)时登录才成功,否则登录 ...

  8. 如何在neo4j中创建新数据库?

    解决方案一: 由于使用Neo3.x创建新数据库而不删除现有数据库,所以只需在$NEO4J_HOME的conf的目录编辑neo4j.conf. 搜寻dbms.active_database=,其默认值应 ...

  9. python语句结构(控制语句与pass语句)

    python语句结构(控制语句和pass语句) break-跳出循环:语句可以跳出for和while语句的循环体.如果你从for和while循环中终止,任何对应循环的else语块均终止 continu ...

  10. 8.关于ActiveMQ、RocketMQ、RabbitMQ、Kafka一些总结和区别

    这是一篇分享文 转自:http://www.cnblogs.com/williamjie/p/9481780.html  尊重原作,谢谢 消息队列 为什么写这篇文章? 博主有两位朋友分别是小A和小B: ...