打印
[牛人杂谈]

函数与指针

[复制链接]
639|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
稳稳の幸福|  楼主 | 2017-4-26 22:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 稳稳の幸福 于 2017-4-27 20:13 编辑

程序中的栈和堆
  • 程序帧是支持函数执行的内存区域,与堆共享内存区域
  • 栈在程序帧下部,由下往上增长;堆在上部,由上往下增长
  • 栈存放函数参数和局部变量,堆管理动态内存
  • 栈帧保存以下元素
    • 返回地址
    • 局部数据存储
    • 参数存储
    • 栈指针和基指针


函数参数指针传递
  • 函数参数传递时,实参传递给形参的是值传递,这会复制实参,不够高效,尤其是传递结构体时,我们不希望再一次复制完全的结构体。而使用指针传递,不但可以改变实参,还能更高效的传递参数。如传递结构体时,传递结构体指针即可,无需其他消耗。
  • 如果传递时,使用指向常量的指针,则可以更高效的获取实参内容,而且不会改变实参内容。传递指针的指针对于以下代码

[size=0.85em]void allocateArray(int *arr, int size, int value){    arr=(int *)malloc(size * sizeof(int));    for(i=0;i<size;++i)        *(arr+i)=value;}int *vector = NULL;allocateArray(&vector,10,1);
会产生问题
  • 首先函数内部虽然将地址传给了[size=0.85em]arr,但是紧接着[size=0.85em]arr的地址被重新通过[size=0.85em]malloc赋值,所以并没有使用[size=0.85em]vector的内容
  • 函数释放后,分配的空间地址丢失,导致内存泄漏

为了解决以上的问题,我们使用指向指针的指针。
[td]
想改变的实参类型形参形参的形式
intint *
int *int **
  • 当函数改变的实参的值时,而不是形参创建的副本,那么函数传递就要传递实参的地址
  • 当函数要改变的是实参本身就是指针,那么函数就要传递指针的地址,也就是函数的形参要定义成指向指针的指针。

所以修改代码
[size=0.85em]void allocateArray(int **arr, int size, int value){    *arr=(int *)malloc(size * sizeof(int));    for(i=0;i<size;++i)        *(*arr+i)=value;}int *vector = NULL;allocateArray(&vector,10,1);
这样调用,函数内部分配的内存地址会直接传递给实参vector,不会导致地址的丢失等问题
函数返回指针
  • 声明[size=0.85em]int * fcuntion()即意味着此函数返回的是一个整型指针。[size=0.85em]注意与int (*fcuntion)()的区别常用的函数返回指针技术常用的返回指针技术有两种:
  • 函数内部使用[size=0.85em]malloc分配内存空间。调用者负责释放内存

[size=0.85em]int * allocateArray(int size, int value){    int *arr = malloc(size * sizeof(int));    for(int i=0;i<size;++i)        *(arr+i)=value;    return arr;}
然后使用以下方式调用
int *vector = allocateArray(10,1);for(i=0;i<10;++i)    printf("%p",*(vector+i));
最后要注意一定要释放内存,因为函数内部只负责了分配内存,要由调用者负责释放
free(vector);
  • 函数内部只负责修改传递过来的指针并修改它,内存的分配和释放都有调用者负责。对于传递过来的指针,优先判断是否为空是个好习惯。如下例:

int * allocateArray(int *arr, int size, int value){   if(arr != NULL)    {        for(int i=0;i<size;++i)            *(arr+i)=value;    }    return arr;}
然后使用以下方式调用
[size=0.85em]int* vector=(int *)malloc(size * sizeof(int));allocateArray(vector,10,1);
书中说此方法不推荐,目前不知道为什么
以上两种方式,函数内部使用的内存都是处于堆区域,所以即使函数结束,弹出栈,堆区域的内容仍然存在,只要保留地址即可。但是函数返回指针不要返回函数内部定义的变量,这样函数结束弹出栈帧以后,变量立即消失,这样就出现问题
函数返回指针注意的问题
函数返回指针要注意四个事项:
  • 返回未初始化的指针
  • 返回指向无效地址的指针
  • 返回局部变量指针
  • 返回指针但是外部调用没有释放

在处理函数返回指针时,一定要根据内存方式注意以上四项。
完善的free函数
内置free()函数存在以下问题:
  • 不会检查传入指针是否为空
  • 释放后不会将指针置为NULL 这时候可以创建自己的free函数

void safeFree(void **p){    if(p != NULL && *P != NULL){        free(*p);        *p =NULL;    }}
其中void指针意味着可以传入任何指针类型,定义**p是因为释放的本身是个指针,需要使用传递指针的指针来真正操作指针。
更快的方式可以定义一个宏 #define safeFree(p) safeFree(void ** (&p)) 配合safeFree函数使用。
函数指针
  • 函数指针定义的方式就是 [size=0.85em]int (*function)(),这时候[size=0.85em]function就作为一个指针指向了函数的地址。
  • 函数指针对性能是有一定影响的,使用它处理器就无法配合流水分支预测
  • 正常的函数声明中[size=0.85em]int func(),函数名[size=0.85em]func没有明确定义是指针,还是其他类型。但是对打印函数名[size=0.85em]func的地址,或者函数名取值后[size=0.85em]&func的地址都是同一个值。所以我们只要知道正常的函数声明中,函数名可以等效为该函数的地址。
  • 对于函数指针,建议使用[size=0.85em]fptr作为前缀
  • 使用了函数指针,使用该指针调用函数,程序将不检查参数传递的是否正确
  • 为函数指针声明一个类型定义会比较方便[size=0.85em]typedef int (*fptrfunc)(int,int)
  • 函数指针可以实现在函数调用中动态的调用其他函数,实现了函数作为参数的传递,这样可以使用C语言进行函数式编程。个人感觉函数指针是实现C++的某种基础。例如以下代码

typedef int (* fptrOperation)(int ,int);int sum(int a, int b){ return a+b}int sub(int a, int b){ return a-b}int computer(fptrOperation operaton, int num1, int num 2) { return operaton(num1, num2) }printf("%d\n",computer(sum,1,2));printf("%d\n",computer(sub,5,3));
  • 返回函数指针即用函数指针去声明一个函数,例[size=0.85em]fptrOperation switchcode(char opecode)
  • 当用函数指针去声明一个数组时,那么数组中的每个元素,都代表函数指针指向的一个函数操作,大大增加了函数调用的丰富性。例如

fptrOperation operaton[10]={NULL};
沙发
xixi2017| | 2017-4-27 20:20 | 只看该作者
void allocateArray(int *arr, int size, int value)
{   
arr=(int *)malloc(size * sizeof(int));  
  for(i=0;i<size;++i)      
                   *(arr+i)=value;
}
int *vector = NULL;
allocateArray(&vector,10,1);

使用特权

评论回复
板凳
xixi2017| | 2017-4-27 20:22 | 只看该作者
typedef int (* fptrOperation)(int ,int);
int sum(int a, int b){ return a+b}
int sub(int a, int b){ return a-b}
int computer(fptrOperation operaton, int num1, int num 2)
{
return operaton(num1, num2)
}
printf("%d\n",computer(sum,1,2));
printf("%d\n",computer(sub,5,3));

使用特权

评论回复
地板
huangcunxiake| | 2017-4-27 21:39 | 只看该作者
不建议随便用关于函数的指针,变的跳飞了。

使用特权

评论回复
5
dongnanxibei| | 2017-4-29 19:11 | 只看该作者
函数内部只负责修改传递过来的指针并修改它,内存的分配和释放都有调用者负责

使用特权

评论回复
6
yiyigirl2014| | 2017-4-30 23:11 | 只看该作者
如传递结构体时,传递结构体指针即可,无需其他消耗。

使用特权

评论回复
7
598330983| | 2017-5-3 23:36 | 只看该作者
没用过这种

使用特权

评论回复
8
yiy| | 2017-5-7 16:35 | 只看该作者
如果用内存分配函数,那么用完一定要再释放

使用特权

评论回复
9
yiy| | 2017-5-7 16:36 | 只看该作者
如果不用内存分配函数一般应该是局部变量函数结束自动销毁,而全局是始终存在。

使用特权

评论回复
10
gejigeji521| | 2017-5-8 13:38 | 只看该作者
没看出来都是什么鬼。

使用特权

评论回复
11
yiyigirl2014| | 2017-5-14 15:45 | 只看该作者
当函数改变的实参的值时,而不是形参创建的副本,那么函数传递就要传递实参的地址

使用特权

评论回复
12
天灵灵地灵灵| | 2017-5-16 14:39 | 只看该作者
当函数要改变的是实参本身就是指针,那么函数就要传递指针的地址,也就是函数的形参要定义成指向指针的指针。

使用特权

评论回复
13
xixi2017| | 2017-5-16 23:30 | 只看该作者
堆栈堆栈,总是觉得是一个概念,原来不是。

使用特权

评论回复
14
xixi2017| | 2017-5-16 23:36 | 只看该作者
一个指向函数的指针,其中系统都会有个指向main函数的指针。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

175

主题

3190

帖子

8

粉丝