文章目录
8.5 函数指针和返回指针值的函数
8.5.1 用函数指针变量调用函数
一般形式:
数据类型标识符 (*指针变量名)(形参列表);
数据类型标识符→ 函数的返回值类型
形参列表: 可以只有类型说明符,多个类型说名符之间用 , 分割。
例如: int (*p)(int,int,int,int, … ,)
我们可以通过函数指针指向不同的函数来达到调用不同函数的目的。这个是有实际用途的。
一个函数,在编译的时候,系统会给这个函数分配一个入口地址 , 这个入口地址就称为函数的地址/指针, 既然有地址,那么我们可以定义一个指针变量来指向该函数。然后就可以通过该指针变量调用该函数了。
函数本身在程序执行期间都会占用一段内存, 他们有一个起始地址,就可以用一个指针变量指向一个函数,从而通过指针变量来调用它所指向的函数。
例:
- 普通函数调用
#include <stdio.h>
int max(int x, int y)
{
if(x>y)
return x;
return y;
}
int main()
{
int z = max(5,10);
printf("c=%d\n ",z);
return 0;
}
- 通过函数指针变量调用函数
#include <stdio.h>
int max(int x, int y){
if(x>y)
return x;
return y;
}
int main(){
int c;
int (*p)(int x,int y);
p = max; //将函数max的入口地址赋给指针变量p,函数名代表函数的入口地址.
c = (*p)(5,10); //调用*p就是调用函数max . p指向函数max的入口,等价于
// c = max(5,10); 这里的调用只是用*p取代了函数名max
// p不能指向函数中的某条语句哈
}
注意:
int (*p)(int x,int y);
//此处的(*p) 是不可省略的,因为此处代表的就是函数指针
int (*p)(int x,int y);
// *p两侧的括号()不可省略,有()表示*和p先结合代表一个指针变量
不可以写成int * p(int x,int y); 这就成函数说明了.这里的int * 就表示这个函数的返回值是指向整型变量的指针。
p = max;
p = &max; //也可以这样写
//将函数max的入口地址赋给指针变量p,函数名代表函数的入口地址。现在p就是指向函数max的指针变量,p和max都指向函数的开头。
c = (*p)(5,10);
c = p(5,10); //此处的*也是可以省略的,它两是等价的
//调用*p就是调用函数max ,p指向函数max的入口,等价于c = max(5,10); 这里的调用只是用*p取代了函数名max
// p不能指向函数中的某条语句哈, 所以*(p+1)是不合法的;
此处 c = (*p)(5,10) 和 c = p(5,10) 等价
总结:
- 函数的调用,可以通过函数名,也可以通过函数指针调用。
- 对指针函数的指针变量,不能做运算,也没有意义,只是指向函数的这段代码
8.5.2 把指向函数的指针变量作为函数参数
指向函数的指针变量也可以作为另外一个函数的参数,从而实现函数地址的传递
例如: 在A中调用B函数指针所指向的函数
#include <stdio.h>
int max(int x, int y)
{
if(x>y)
return x;
return y;
}
int p_max(int x, int y, int (*mid)(int x,int y)
{
int result = mid(x,y); //调用 函数指针mid所指向的函数
return result;
}
int main(){
int c;
c = p_max(5,10,max);
printf("%d\n",c);
}
定义了一个max比较大小的函数,定义一个函数指针(有三个形参),然后在主函数中调用这个函数指针。
p_max(5,10,max) 这个max就是指向函数的指针变量
#include <stdio.h>
int max(int x, int y)
{
if(x>y)
return x;
return y;
}
int p_max(int x,
int y,
int (*mid)(int x,int y)
)
{
int result = mid(x,y); //调用 函数指针mid所指向的函数
return result;
}
int main(){
int c;
c = p_max(5,10,max);
printf("%d\n",c);
int (*p)(int,int); //函数指针
p = max;
c = p_max(4,5,p);
printf("%d\n",c);
}
也可以函数中直接调用函数,但是这种函数指针的方式更加灵活友好。
8.5.3 返回指针值的函数
一般返回的值有:
可返回各种类型
return;
如果要返回值的话:
ruturn 1;
函数中可以返回指针型数据,也就是返回一个地址。
返回指针值的函数的一般形式:
数据类型 *函数名(参数列表)
要区分函数指针的定义与返回指针值的函数:
函数指针的定义:
*数据类型 (函数名)(参数列表)
*int (a)(int x, int y)
返回指针值的函数:
*数据类型 函数名 (参数列表)
*int a(int x,int y);
()的优先级高于 * 因此a先和()结合,这就是函数形式 ,返回值为整形指针
返回指针值的函数(坑)
int *add(int x, int y)
{
int sum = x + y;
return ∑ //表面看没问题
}
int main(){
int *presult;
presult = add(4,5); //执行add后,presult指向的内存已经不归你所有,
//你不应该从中取得值或者给其赋值。
printf("%d\n",*presult);
程序中存在致命问题:
输出是没问题的,但是程序调用完了之后sum所占的内存就会被释放。释放之后这段内存不属于你了。你不能动它。
绝对不可以把sum的内存地址返回到被调用函数中并加以使用。
return ∑ //add函数调用完毕后,sum的内存会被系统回收。
那如何改变呢:
全局变量一直到程序结束都是占用内存的。能够被控制,不会被系统回收。
int sum; //全局变量
int *add(int x, int y)
{
sum = x + y;
return ∑
}
int main(){
int *presult;
presult = add(4,5);
printf("%d\n",*presult);
8.6 指针数组、指针的指针、main函数参数、小节
8.6.1 指针数组概念回顾
指针数组:
其元素都为指针类型数据,称为指针数组。
指针数组中每一个元素都是指针变量。
指针数组的定义形式:
类型标识符 *数组名[数组长度说明]
int *p[4]; //每个数组元素都可以看成是一批
//指针变量,都可以指向一个整形普通变量
数组指针:
int (*p)[4]; //表示这是指向一维数组的指针变量。也就是说它是**一个
// 指针变量,**
指针数组回顾:
#include "stdio.h"
int main(){
const char * pname[] = {"c++","java","c#","go","shell"}; //指针数组
int is1 = sizeof(pname); //每个指针变量占4个字节,有5个元素,所以一共占用20个字节
int isize = sizeof(pname) / sizeof(pname[0]);
// 20/4=5; 表示pname中有5个元素。能引用的下标就是【0】-【4】
printf("%d",isize);
const char* p2 ="java";// p2=> pname[1]
//常量会有一个固定的存放地址。
int i;
for(i=0;i<isize;i++)
{
printf("pname[%d] = %s\n",i,pname[i]);
}
printf("-------------\n");
const char *ptemp;
ptemp = pname[0]; //ptemp 指向了c++
pname[0] = pname[1];
pname[1] = ptemp;
//相当于pname[1]指向了c++ ;
for(i=0;i<isize;i++)
{
printf("pname[%d] = %s\n",i,pname[i]);
}
}
8.6.2 指向指针的指针
用来指向指针变量的变量
简称 指向指针的指针。
char **p; // 定义了一个指向**字符串指针变量**的指针变量。
int **p; //定义了一个指向**整形指针变量**的指针变量。
**p → 从右到左结合 ==》 *(*p); 表示指针变量p是指向指针变量的。
#include "stdio.h"
int main(){
const char * pname[] = {"c++","java","c#","go","shell"}; //指针数组
int is1 = sizeof(pname); //每个指针变量占4个字节,有5个元素,所以一共占用20个字节
int isize = sizeof(pname) / sizeof(pname[0]);
// 20/4=5; 表示pname中有5个元素。能引用的下标就是【0】-【4】
printf("%d",isize);
const char* p2 ="java";// p2=> pname[1]
//常量会有一个固定的存放地址。
int i;
for(i=0;i<isize;i++)
{
printf("pname[%d] = %s\n",i,pname[i]);
}
printf("-------------\n");
const char *ptemp;
ptemp = pname[0]; //ptemp 指向了c++
pname[0] = pname[1];
pname[1] = ptemp;
//相当于pname[1]指向了c++ ;
for(i=0;i<isize;i++)
{
printf("pname[%d] = %s\n",i,pname[i]);
}
printf("--------------------------------------");
char **pp;
pp = &pname[0]; //*pp 就是pp所指向的指针,也就是pname[0]
printf"
}
指向指针的指针深入探究
#include "stdio.h"
int main(){
const char * pname[] = {"c++","java","c#","go","shell"}; //指针数组
int is1 = sizeof(pname); //每个指针变量占4个字节,有5个元素,所以一共占用20个字节
int isize = sizeof(pname) / sizeof(pname[0]);
// 20/4=5; 表示pname中有5个元素。能引用的下标就是【0】-【4】
printf("%d",isize);
const char* p2 ="java";// p2=> pname[1]
//常量会有一个固定的存放地址。
int i;
for(i=0;i<isize;i++)
{
printf("pname[%d] = %s\n",i,pname[i]);
}
printf("-------------\n");
const char *ptemp;
ptemp = pname[0]; //ptemp 指向了c++
pname[0] = pname[1];
pname[1] = ptemp;
//相当于pname[1]指向了c++ ;
for(i=0;i<isize;i++)
{
printf("pname[%d] = %s\n",i,pname[i]);
}
const char** pp;
pp = &pname[0];
printf("pp = %s\n",*pp);
printf("\n--------------------\n");
int abc = 5;
int *pabc = &abc; //这是个指向整形数据的指针
int **ppabc = &pabc; //指向指针的指针.
printf("abc = %d\n",abc);
printf("pabc = %d\n",*pabc); //*pabc == abc , **ppabc == *pabc == abc
printf("pabc = %d\n",**ppabc);
}
8.6.3 指针数组做为main函数形参
指针数组有个重要应用: 就是能够做main函数的参数。
main函数是可以有形参的
例:
#include "stdio.h"
int main(int argc, //整形,应该是argv指针数组的元素个数
char *argv[] //就是个指针数组做函数形参 ,argv[0]保存的是 当前可执行文件的完整路径文件名
)
{
int i;
printf("argc=%d\n",argc); //argc我们输入的参数个数+1
for(i=0;i<argc;i++){
//printf("argv[%d]=%s\n",i,argv[i]);
printf("argv[%d] = %s\n",i,*argv);
argv++;
}
}
8.6.4 本章小结
指针数据类型:
指针运算小结
int *p;
p++ ; //加了4个字节才是p++的地址。到底加多少要看他的类型
指针变量 的赋值
int *p =1000; //不可以这样,不能直接赋数字,不能将地址直接赋值给p,只能够将变量的已分配的地址给指针便量。
int a ;
int *p = &a;
*p = 30; //这样才可以。
指针变量可以为空值,表示不指向任何变量
int* p = NULL; //NULL相当于整数0. 就是使得p指向地址为0的单元。
//系统会保证地址为0这个单元不存放有效数据。
if(p==NULL)
{
p = &a ;
}
void * 型指针
万能型指针变量, 也就是能够指向任意数据类型;
int a= 3;
int *p=&a;
float bf = 4.5f;
float *pf = &bf;
// p = (int *)pf; //强制类型转换
void *pwn =NULL;
pwn = p;
pwd = pf;
pf = (float *)pwn; //给回的时候需要强制类型转换
总结
指针优点:效率高,
缺点:灵活,初学者易犯错