本帖最后由 稳稳の幸福 于 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]想改变的实参类型 | 形参形参的形式 | int | int * | 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};
|