第八章、指针(下)

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 &sum;  //表面看没问题

}

int main(){

int *presult;

presult = add(4,5);  //执行add后,presult指向的内存已经不归你所有,
//你不应该从中取得值或者给其赋值。

printf("%d\n",*presult);

程序中存在致命问题:

输出是没问题的,但是程序调用完了之后sum所占的内存就会被释放。释放之后这段内存不属于你了。你不能动它。

绝对不可以把sum的内存地址返回到被调用函数中并加以使用。

return &sum; //add函数调用完毕后,sum的内存会被系统回收。

那如何改变呢:

全局变量一直到程序结束都是占用内存的。能够被控制,不会被系统回收。

int sum; //全局变量
int *add(int x, int y)

{

sum  = x  + y;

return &sum;  

}

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;    //给回的时候需要强制类型转换

总结

指针优点:效率高,

缺点:灵活,初学者易犯错

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨优秀&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值