C语言的本质(12)——指针与函数


 往往,我们一提到指针函数和函数指针的时候,就有很多人弄不懂。下面详细为大家介绍C语言中指针函数和函数指针。

 

1、指针函数

 当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中。

 格式: 

类型说明符 * 函数名(参数)

 

由于返回的是一个地址,所以类型说明符一般都是int。

 在C语言中,函数也是一种类型,可以定义指向函数的指针。我们知道,指针变量的内存单元存放一个地址值,而函数指针存放的就是函数的入口地址(位于.text段)。下面看一个简单的例子:

 

 

#include <stdio.h>
 
void say_hello(const char *str)
{
    printf("Hello %s\n", str);
}
 
int main(void)
{
    void (*f)(const char *) = say_hello;
    f("Guys");
    return 0;
}

 

分析一下变量f的类型声明void (*f)(const char *),f首先跟*号结合在一起,因此是一个指针。(*f)外面是一个函数原型的格式,参数是const char *,返回值是void,所以f是指向这种函数的指针。而say_hello的参数是const char *,返回值是void,正好是这种函数,因此f可以指向say_hello。注意,say_hello是一种函数类型,而函数类型和数组类型类似,做右值使用时自动转换成函数指针类型,所以可以直接赋给f,当然也可以写成void (*f)(const char *) = &say_hello;,把函数say_hello先取地址再赋给f,就不需要自动类型转换了。

 

可以直接通过函数指针调用函数,如上面的f("Guys"),也可以先用*f取出它所指的函数类型,再调用函数,即(*f)("Guys")。可以这么理解:函数调用运算符()要求操作数是函数指针,所以f("Guys")是最直接的写法,而say_hello("Guys")或(*f)("Guys")则是把函数类型自动转换成函数指针然后做函数调用。

 

2、函数指针

 指向函数的指针包含了函数的地址,可以通过它来调用函数。声明格式如下: 

类型说明符 (*函数名)(参数)

其实这里不能称为函数名,应该叫做指针的变量名。这个特殊的指针指向一个返回整型值的函数。指针的声明笔削和它指向函数的声明保持一致。

 指针名和指针运算符外面的括号改变了默认的运算符优先级。如果没有圆括号,就变成了一个返回整型指针的函数的原型声明。

 例如:

 

void (*fptr)();


把函数的地址赋值给函数指针,可以采用下面两种形式:

 

fptr=&Function; 
fptr=Function;


取地址运算符&不是必需的,因为单单一个函数标识符就标号表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。

可以采用如下两种方式来通过指针调用函数:

 

x=(*fptr)(); 
x=fptr();


第二种格式看上去和函数调用无异。但是有些程序员倾向于使用第一种格式,因为它明确指出是通过指针而非函数名来调用函数的。下面举一个例子:

 

void (*funcp)(); 
void FileFunc(),EditFunc(); 
int main(void) 
{ 
	funcp=FileFunc; 
	(*funcp)(); 
	funcp=EditFunc; 
	(*funcp)(); 
} 
void FileFunc() 
{  
	printf(\"FileFunc\\n\"); 
} 
void EditFunc() 
{ 
	printf(\"EditFunc\\n\"); 
}

程序输出为:

 FileFunc 

EditFunc

  

下面再举几个例子区分函数类型和函数指针类型。首先定义函数类型F:

 

typedef int F(void);


这种类型的函数不带参数,返回值是int。那么可以这样声明f和g:

 F f, g;相当于声明:

int f(void);
int g(void);


下面这个函数声明是错误的:

 

F h(void);


因为函数可以返回void类型、标量类型、结构体、联合体,但不能返回函数类型,也不能返回数组类型。而下面这个函数声明是正确的:

 

F *e(void);


函数e返回一个F *类型的函数指针。如果给e多套几层括号仍然表示同样的意思:

 

F *((e))(void);


但如果把*号也套在括号里就不一样了:

int (*fp)(void);

这样声明了一个函数指针,而不是声明一个函数。fp也可以这样声明:

F *fp;



3、指针类型的参数和返回值

 首先看下面的程序:

 

#include <stdio.h>
 
int *swap(int *px, int *py)
{
         inttemp;
         temp= *px;
         *px= *py;
         *py= temp;
         returnpx;
}
 
int main(void)
{
         inti = 10, j = 20;
         int*p = swap(&i, &j);
         printf("nowi=%d j=%d *p=%d\n", i, j, *p);
         return0;
}
 

我们知道,调用函数的传参过程相当于用实参定义并初始化形参,swap(&i, &j)这个调用相当于:

int *px = &i;
int *py = &j;

所以px和py分别指向main函数的局部变量i和j,在swap函数中读写*px和*py其实是读写main函数的i和j。尽管在swap函数的作用域中访问不到i和j这两个变量名,却可以通过地址访问它们,最终swap函数将i和j的值做了交换。

 

上面的例子还演示了函数返回值是指针的情况,return px;语句相当于定义了一个临时变量并用px初始化: 

int *tmp = px;

然后临时变量tmp的值成为表达式swap(&i,&j)的值,然后在main函数中又把这个值赋给了p,相当于: 

int *p = tmp;

最后的结果是swap函数的px指向哪就让main函数的p指向哪。我们知道px指向i,所以p也指向i。

 

 

 

 

转载于:https://www.cnblogs.com/new0801/p/6177114.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值