指针和函数
一、程序栈
指针对函数功能的贡献极大,它们能够将数据传递给函数,并且允许函数对数据进行修改。我们可以将复杂数据用结构体指针的形式传递给函数和从函数返回,如果指针持有函数的地址,就能动态控制程序的执行流。
要理解函数及其和指针的结合使用,需要理解程序栈,调用函数的时候,会创建函数的栈帧并将其推到程序栈上,函数返回时,其栈帧从程序栈上弹出。
在使用函数时,有两种情况指针很有用。首先是将指针传递给函数,这是函数可以修改指针所引用的数据,也可以高效地传递大块信息。另外一种情况是声明函数指针,本质上,函数表示法就是指针表示法,函数名字经过求值变成函数的地址,然后函数参数会被传递给参数。
程序栈是支持函数执行的内存区域,通常和堆共享,也就是说,他们共享同一块内存区域,程序栈通常占据这块区域的下部,而堆用的则是上部。
栈帧的组织由以下几种元素组成(返回地址、局部数据存储、参数存储、栈指针和基指针)
(1) 返回地址
函数完成后要返回的程序内部地址
(2) 局部数据存储
为局部变量分配的内存
(3) 参数存储
为函数参数分配的内存
(4) 栈指针和基指针
运行时系统用来管理栈的指针。栈指针通常指向栈顶部,基指针通常存在并执行栈帧内部的地址,比如返回地址,用来协助访问栈帧内部的元素。这两个指针都不是c指针,他们是运行时返回管理程序栈的地址。
二、 通过指针传递和返回数据
传递指针可以让多个函数访问指针所引用的对象,而不是把对象声明为全局可访问,这意味着只有需要访问这个对象的函数才有访问权限,而且也不需要复制对象。
1、指针传递数据
void test(int *px,int *py)
{
int x;
x=*px;
*px=*py;
*py=x;
}
int *plobal;
int main(int argc, char* argv[])
{
int x=1;
int y=2;
test(&x,&y);
printf("x=%d\n",x);
printf("y=%d\n",y);
return 0;
}
2、传递指向常量的指针(就是px指向的值数据不能被修改)
void test(const int *px,int *py)
{
*py=*px;
}
int *plobal;
int main(int argc, char* argv[])
{
int x=1;
int y=2;
test(&x,&y);
printf("x=%d\n",x);
printf("y=%d\n",y);
return 0;
}
3、返回指针
返回指针很容易,只要返回的类型是某种数据类型的指针即可。
(1) 使用malloc在函数内部分配内存并返回其地址,调用者负责释放返回的内存
下面的例子,定义一个函数,为其传递一个整数数组的长度和一个值初始化每个元素,函数为整数数组分配内存,用传入的值进行初始化,然后返回数组地址。
int* test(int size,int value)
{
int* arr =(int*)malloc(size*sizeof(int));
for(inti=0;i<size;i++)
{
*(arr+i)=value+i;
}
return arr;
}
int *plobal;
int main(int argc, char* argv[])
{
int*px=test(2,4);
return 0;
}
最后,还得记得free(px).
(2) 传递一个指针对象给函数并让函数修改它,这样分配和释放对象的内存都是调用者的责任
int* test(int *arr,int size,int value)
{
if(arr!=NULL)
{
for(inti=0;i<size;i++)
{
arr[i]=value;
}
}
return arr;
}
int * plobal;
int main(int argc, char* argv[])
{
int*vector=(int*)malloc(5*sizeof(int));
int*px=test(vector,5,45);
for(inti=0;i<5;i++)
{
printf("%d\n",px[i]);
}
return 0;
}
(3) 局部数据指针(不要返回局部变量的地址,因为局部变量的内存会被后续的函数调用中被覆盖)
在下面的程序中,一旦函数返回,返回的数组地址就无效,可能数组的长度仍然不变,但是其值已经被覆写了。
int* test(int size,int value)
{
int temp=size;
intarr[45];
for(int i=0;i<45;i++)
arr[i]=value+i;
return arr;
}
int * plobal;
int main(int argc, char* argv[])
{
int*px=test(2,4);
for(inti=0;i<45;i++)
{
printf("%d\n",px[i]);
}
return 0;
}
(4) 输入参数是指针变量,函数内部可以修改指针的引用值,当不要修改指针的值
(5) 输入参数是指针的指针
将指针传递给函数时,传递的是值。如果我们想修改原指针,而不是指针的副本,就需要传递指针的指针。在下例中,我们传递了一个整数数组的指针,为该数组分配内存并将其初始化,函数会用第一个参数返回分配的内存。在函数中,我们先分配内存,然后初始化,所分配的内存地址应该被赋给一个整数指针。为了在调用函数中修改这个指针,我们需要传入指针的地址,所以,参数被声明为int指针的指针。在调用函数中,我们需要传递指针的地址:
三、函数指针
函数指针是持有函数地址的指针,指针能够指向函数。使用函数指针的一个顾虑是:这种做法可能会导致程序运行变慢,处理器可能无法配合流水线做分配预测。
1、声明函数指针
void (*foo)();
声明一个名为foo的函数指针,星号表示这是个指针,void表示返回类型,(*foo)表示函数指针变量的名字,()表示参数。声明函数指针一定要小心,因为C不会检查参数传递是否正确。
2、使用函数指针
先声明函数指针:int (*fptr1)(int);
int test(int x)
{
return x*x;
}
int main(int argc, char* argv[])
{
int (*fptr1)(int);
int n=5;
fptr1=test;//也可以这样赋值,但是没有必要fptr1 = &test
printf("%dsquare is %d\n",n,fptr1(n));
return 0;
}
3、传递函数指针,即将函数指针作为参数
#include "stdafx.h"
#include<iostream>
#define MAXSIZE100 //定义线性表的最大长度
int add(int num1,int num2)
{
return num1+num2;
}
int subtract(int num1 ,int num2)
{
return num1-num2;
}
typedef int(*fptroperation)(int,int);//定义一个指针函数
int compute(fptroperation operation,int num1,int num2)//指针函数作为形参
{
return operation(num1,num2);
}
int main(int argc, char* argv[])
{
printf("%d\n",compute(add,5,6));//调用该函数
printf("%d\n",compute(subtract,5,6));//调用该函数
return 0;
}
4、返回函数指针
返回函数指针需要把函数的返回类型声明为函数指针,
四、总结