指针函数和函数指针

1.指针函数

(1)本质是一个函数,不过它的返回值是一个指针。其声明的形式:类型名 *函数名(函数参数列表);

int * pfun(int, int);

由于“*”的优先级低于“()”的优先级,因而pfun首先和后面的“()”结合,也就意味着,pfun是一个函数;

接着再和前面的“*”结合,说明这个函数的返回值是一个指针。由于前面还有一个int,也就是说,pfun是一个返回值为整型指针的函数

(2)返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”。

(3)void型指针

        void指针是一种不明确类型的指针,任何指针都可转换为void指针。

       指针有两个非常重要的信息:

  1. 指针的值(指针目标对象的内存首地址)
  2. 指针指向对象的类型

        注意点:void指针只保存了 指针的值 并没有记录 指针指向对象的类型。因此在用到对void指针解引时,需要先把void指针转换成原本的数据类型。

        

int n = 500; //定义一个int变量
 
int * p = &n; //定义int类型指针
 
void * pv = p; //定义void指针,只保存了p的值(即n的内存首地址)
 
//错误的写法
 
printf("%d\n", *pv); //这里会报错,因pv指针没有明确数据类型,因此也不知道需要取多少字节的数据
 
//正确写法
 
printf("%d\n", *( (int*)pv ) ); //先把pv指针转为int类型指针,再对其解引

(4)举例:

//打印第m个学生的成绩
#include <stdio.h>
 float *find(float(*pionter)[4],int n);//函数声明
 int main(void)
 {
     static float score[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}};//3个学生的成绩
     float *p;                                 //定义float类型的指针
     int i,m;
     printf("Enter the number to be found:");
     scanf("%d",&m);
     printf("the score of NO.%d are:\n",m);
     p=find(score,m-1);                       //将find函数返回的地址给p
     for(i=0;i<4;i++)
         printf("%5.2f\t",*(p+i));            //打印(score+n)行i列数据
  
     return 0;
 }
 
float *find(float(*pionter)[4],int n)/*定义指针函数*/
 {
     float *pt;                             //定义float类型的指针
     pt=*(pionter+n);                       //将(pionter+n)行的首地址给pt
     return(pt);                            //返回float类型的地址
 }

共有三个学生的成绩,函数find()被定义为指针函数,其形参pointer是指针指向包含4个元素的一维数组的指针变量(即简称数组指针)

补充:为什么*(point+n)是表示地址?而不是取值

        *(point+1)单独使用时表示的是第 1行数据放在表达式中会被转换为第 1 行数据的首地址,也就是第 1 行第 0 个元素的地址。因为使用整行数据没有实际的含义,编译器遇到这种情况都会转换为指向该行第 0 个元素的指针;就像一维数组的名字,在定义时或者和 sizeof、& 一起使用时才表示整个数组,出现在表达式中就会被转换为指向数组第 0 个元素的指针。

main()函数中调用find()函数,将score数组的首地址传给pointer。

2.函数指针

(1)函数指针是指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。

C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。

(2)函数指针的声明方法为:

返回值类型 ( * 指针变量名) ([形参列表]);

注1:“返回值类型”说明函数的返回类型,“(指针变量名 )”中的括号不能省,括号改变了运算符的优先级。若省略整体则成为一个函数说明,说明了一个返回的数据类型是指针的函数,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例如:

int func(int x); /* 声明一个函数 */

int (*f) (int x); /* 声明一个函数指针,是一个指向返回值为int的函数的指针 */

f=func; /* 将func函数的首地址赋给指针f */

或者使用下面的方法将函数地址赋给函数指针:

f = &func;

赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。

(3)举例:

#include<stdio.h>
int max(int x,int y){return (x>y? x:y);}  //返回两个数的最大值
int main()
{
    int (*ptr)(int, int);                //定义指向函数的指针变量
    int a, b, c;
    ptr = max;                           //将max函数的地址给ptr
    scanf("%d%d", &a, &b);
    c = (*ptr)(a,b);                     //ptr和max函数地址相同,相当于调用max函数
    printf("a=%d, b=%d, max=%d", a, b, c);
    return 0;
}

ptr是指向函数的指针变量,所以可把函数max赋给ptr,作为ptr的值,即把max函数的入口地址赋给ptr,以后就可以用ptr来调用该函数,实际上ptr和max都指向同一个入口地址。不同就是ptr是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你想怎么做了。在程序中把哪个函数的地址赋给它,它就指向哪个函数。而后用指针变量调用它,因此可以先后指向不同的函数。不过注意,指向函数的指针变量没有++和--运算,用时要小心。

(4)补充

  1. 函数指针可作为参数
  2. 函数指针可作为返回值
#include <stdio.h>

int add(int num1,int num2)
{
	return num1+num2;
}

int sub(int num1,int num2)
{
	return num1-num2;
}

int fun(int (*fp)(int,int),int num1,int num2)  //函数指针做参数
{
	return (*fp)(num1,num2);
}


int (*select(char c))(int,int)           //函数指针作为返回值
{

	switch(c)
	{
		case '+': return add;
		case '-': return sub;
	}

}


int main()
{
	
	int num1,op,num2;
	int (*fp)(int,int);

	printf("请输入一个表达式,比如(1+3):\n");
	scanf("%d%c%d",&num1,&op,&num2);

	fp=select(op);  //返回的函数指针赋予fp

	printf("%d%c%d=%d\n",num1,op,num2,fun(fp,num1,num2));

	/*
	printf("3+5=%d\n",fun(add,3,5));  函数名就是函数的首地址
	printf("3-5=%d\n",fun(sub,3,5));
	*/

	return 0;
}

int (*add)(int, int);定义了一个函数指针add,用于指向返回值为int,并且有两个int参数的函数

int (*select(char c))(int,int) 分解如下:

  • select与后面的(char c)结合,说明是一个函数,即select(char c)
  • 在和*结合,说明select函数的返回值是一个指针,即*(select(char c))
  • 在和后面的(int,int)结合,说明select函数返回的指针指向函数,不是指向int类型,即int ((*select(char c)))(int,int)
  • 总结:返回值为select函数指针,该返回值指向一个 返回值为int,两个参数为int类型的函数

(5)使用typedef关键字

int (*PF)(int *, int);     PF是一个函数指针变量,用于指向返回值为int,一个int类型参数的函数。

当使用typedef声明后,则PF就成为了一个函数指针类型,即  typedef int (*PF)(int *, int);  这样就定义了返回值的类型。

再用PF作为返回值来声明函数:PF func(int);      //  func(int)就是一个返回值为函数指针,一个int类型参数的函数

再用PF来声明:PF phead;                                 //phead就是一个函数指针

(6)地址直接操作函数和指针

if((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)

其中:

*(vu32*)(0X20001000+4))== (*(__IO uint32_t*)(0X20001000+4))==(*(volatile unsigned int*)(0X20001000+4))

(*(vu32*)(0X20001000+4)) 通过内存寻址访问地址为(0x20001000 + 4)中的值

(0X20001000+4)只是一个常量;

(volatile unsigned int*)(0X20001000+4) 将0x20001000 + 4这个常量强制转化成volatile unsigned int类型的指针;

(*(volatile unsigned int*)(0X20001000+4)) 相对于取0x20001000 + 4地址处的值。


为什么要将0x20001000 + 4这个常量强制转化成volatile unsigned int类型的指针呢?

       假设定义*P , 取地址符 &P 得到P地址,这个地址是随机的系统分配空闲地址。我们不能直接给P赋地址值,因为那样是不合法的,我们只能给指针赋值为NULL。

但是现在必须让指针指向一个已知地址(0X20001000+4),必须转换类型,在地址前面加上指针转换的类型我们这里用的volatile unsigned int*,如果不转换,不成功。
 

假如使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义:

    #define RAM_ADDR     (*(volatile unsigned long *)0x01234567)
    然后就可以用C语言对这个内存地址进行读写操作了。
    读:tmp = RAM_ADDR;
    写:RAM_ADDR = 0x55;


这里使用  volatile关键字的好处就是:

1.volatile是一个类型修饰符(typespecifier);

2.volatile关键字声明的变量,编译器对访问该变量的代码就不再进行优化;

3.volatile 关键字声明的变量,对变量的存取不能缓存到寄存器,每次使用时需要在内存中重新存取。

(7)使用typedef定义函数指针

typedef的意义

typedef int a[10];          // a 类型是 int[10];(存放int型数据的数组)
a arr;                      // 定义一个数组:int arr[3];

typedef void (*p)(void);    //p 类型是void ( * )void
p A;                        //是指void(*A)(void);

语法上typedef属于存储类声明说明符。
a[10]不是int的别名,(*p)(void)不是void的别名。
上面的语句把a声明为具有10个int元素的数组的类型别名,p是一种函数指针的类型别名。

函数指针对象赋值用法

两种用法

typedef void (*IapFun)(void);  //定义函数指针
void func(void);               //定义函数
IapFun fun  = func;            //为函数指针对象赋值
fun();                         //这里的fun()其实就相当于跳转到了func()里
typedef void (* IapFun)(void);          //定义函数指针
IapFun jump2app;                         //定义函数指针对象
jump2app=(IapFun) * (vu32*)(appxaddr+4); //为函数指针对象赋值 appxaddr为函数指针地址,例如0x08000000
jump2app();                              //调用函数

  • 12
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值