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。
C语言的本质(12)——指针与函数的更多相关文章
- 【C语言】14-返回指针的函数与指向函数的指针
前言 前面我们花了接近3个章节学习指针,应该都感受到指针的强大了吧.指针可以根据地址直接操作内存中的数据,使用得当的话,不仅能使代码量变少,还能优化内存管理.提升程序性能.关于指针的内容还非常多,比如 ...
- 我的c++学习(12)指针作为函数参数
◆ 引用调用与指针传值调用C++中函数的参数的基本使用方法是传值.为了弥补单纯传值的不足,以引用作为函数的参数,从逻辑上讲引用是别名,在函数中对参数的操作,就是对实参的操作,而在物理上是传实参的地址. ...
- go语言基础之数组指针做函数参数
1.数组指针做函数参数 示例: package main //必须有个main包 import "fmt" //p指向实现数组a,它是指向数组,它是数组指针 //*p代表指针所指向 ...
- C语言的本质(18)——函数的可变参数
一般而言,在设计函数时会遇到许多数学和逻辑操作,是需要一些可变功能.例如,计算数字串的总和.字符串的联接或其它操作过程. 实现一个函数,要求在函数中计算传入的所有参数之和,并输出到屏幕上.这个函数实现 ...
- C语言的本质(16)——函数接口的传入参数与传出参数
如果函数接口有指针参数,既可以把指针所指向的数据传给函数使用(称为传入参数),也可以由函数填充指针所指的内存空间,传回给调用者使用(称为传出参数),例如strcpy的函数原型为 char *strcp ...
- C语言笔记 07_枚举&指针
emum(枚举) 枚举是 C 语言中的一种基本数据类型,它可以让数据更简洁,更易读. 枚举语法定义格式为: enum 枚举名 {枚举元素1,枚举元素2,--}; 举个例子,比如:一星期有 7 天,如果 ...
- 【C语言】学不会的指针
指针 前言: 指针是C语言程序的核心,刚开始学指针,嗯....这样呀,貌似不难呀:之后开始用指针,&p,p,*p,**p,这些指针在用的时候,额.....什么东东?每次都要想半天,特别是遇到双 ...
- 12-返回指针的函数&&指向函数的指针
前言 接下来我只讲指针的最常见用法,比如这一章的内容----返回指针的函数 与 指向函数的指针 一.返回指针的函数 指针也是C语言中的一种数据类型,因此一个函数的返回值肯定可以是指针类型的. 返回 ...
- C语言的本质(15)——C语言的函数接口入门
C语言的本质(15)--C语言的函数接口 函数的调用者和其实现者之间存在一个协议,在调用函数之前,调用者要为实现者提供某些条件,在函数返回时,实现者完成调用者需要的功能. 函数接口通过函数名,参数和返 ...
随机推荐
- centos安装phpMyAdmin
phpMyAdmin是一个网络接口,通过它可以管理你的MySQL数据库. 首先,我们使CentOS系统RPMForge软件库的phpMyAdmin,而不是官方的CentOS 6.2库: 所以需要导入 ...
- 七夕节(hd1215)干嘛今天做这题T_T
七夕节 Problem Description 七夕节那天,月老来到数字王国,他在城门上贴了一张告示,并且和数字王国的人们说:"你们想知道你们的另一半是谁吗?那就按照告示上的方法去找吧!&q ...
- 山寨QQ音乐的布局(一)
学了两天IOS趁着还没忘光,巩固一下所学知识想做点东西,由于自己的设计能力有限,所以就山寨一下吧,说到山寨怎么能忘了腾讯呢,今天发现QQ音乐的设计风格是扁平化的,小清新风格,所以就山寨一下它吧.. 由 ...
- 强化一下,QDialog有专门的exec函数和finished信号,QWidget都没有
http://blog.csdn.net/dbzhang800/article/details/6300519
- linux下设置ip地址 gw网关,dns的方法
本文介绍下,在linux中设置IP地址.网关.dns的方法,有需要的朋友作个参考吧. 设置linux网络的方法有两种:第一种:使用命令修改(直接即时生效) 复制代码代码示例: ip and net ...
- 续前篇---数据挖掘之聚类算法k-mediod(PAM)原理及实现
上一篇博文中介绍了聚类算法中的kmeans算法.无可非议kmeans由于其算法简单加之分类效率较高 已经广泛应用于聚类应用中. 然而kmeans并非十全十美的.其对于数据中的噪声和孤立点的聚类带来的误 ...
- 2.5 Local Methods in High Dimensions
curse of dimensionality 输入在p维立方体中符合均匀分布,如果需要覆盖比例r的体积,需要每个维度上\(e_p(r)=r^{1/p}\) \(e_{10}(0.01)=0.63,e ...
- IOS开发之——获取屏幕的尺寸及各模拟器代表的型号
获取屏幕尺寸 [[[UIScreen mainScreen] currentMode].size.width]; [[[UIScreen mainScreen] currentMode].size.h ...
- jQuery下实现检测指定元素加载完毕
检测元素出现方法.虽然是基于 jQuery 的,但是代码很简洁,可以修改成纯js版的. 文本 jQuery.fn.wait = function (func, times, interval) { v ...
- 【稳定婚姻问题】【HDU1435】【Stable Match】
2015/7/1 19:48 题意:给一个带权二分图 求稳定匹配 稳定的意义是对于某2个匹配,比如,( a ---- 1) ,(b----2) , 如果 (a,2)<(a,1) 且(2,a)& ...