1、函数原型的作用
函数原型保证以下几点
2、指针常量和常量指针
常量指针意味着指针指向的地址中的值不能变
int a=3;
const int *p=&a;
则无法通过指针p修改变量a的值。可以将常量地址赋给常量指针,但不能赋给常规指针,防止常规指针修改常量。当只有一级关系时,可以将非const指针赋值给const指针,但进入两级关系时,不允许这样做
//const指针
const int **p2;
//常规指针
int * p1;
//常量
const int n=13;
//不合法,假设可以将常规指针赋值给const指针,即*p2=p1;
p2=&p1;
*p2=&n
*p1=10;
这样的话常规指针修改了常量的值
指针常量意味着指针指向的地址不可以改变
int n=13;
const int * p1=&n;
int * const p2=&n;
//*p1和p2为const,而p1和*p2不是
3、数组指针与指针数组
数组指针
int a[4][5];
int (*p1)[5]=a;
由于优先级()>[]>*,所以p与*先结合成指针,int修饰数组内容,p为指针,指向一个5个int类型元素的数组。
指针数组
int a[4][5];
int *p2[4];
p2[0]=a[0];
p与[]结合形成数组,int*修饰整个数组的内容,即数组由4个指向int的指针组成。
区别
数组指针又被称为行指针,*(*(p1+1)+2)等值于a[1][2];而p2不能直接用a赋值,因为他是一个数组。同理,sizeof(p1)为4,而sizeof(p2)为16.
4、函数和二维数组
当函数参数为而为二维数组时,函数原型
int sum(int (*arr)[4],int size);
//int sum(int arr[][4],int size);
//不能是
//int sum(int *arr[4],int size);
无论是第一种还是第二种,都说明arr是一个指针,指向一个由4个int类型元素组成的数组。因此指针指定了列数,而对行数没有做要求,所以单独用size传递行数。
5、函数指针
获取函数指针
假设think()是个函数,则think就是该函数的地址,要将函数作为参数传递,需要传递该函数名。
pocess(think);
pocess的调用使得可以在其内部调用think。
声明函数指针
声明指向的函数指针,必须指定指针指向的函数类型,这意味着应指定返回类型及函数特征标。
//函数原型
double pam(int);
//函数指针
double (*pf)(int);
//double * pf(int);返回指针的函数
这里用括号将*pf括起来是因为()的优先级比*高。
void estimate(int size,double (*pf)(int));
函数的第二个参数是函数指针,它指向的函数接受一个int类型参数,并返回一个double值。
使用函数指针调用函数
double pam(int);
double (*pf)(int);
pf=pam;
double x=pam(4);
double y=(*pf)(5);
double z=pf(6);
可以使用(*pf)和pf调用pam函数。
#include
double betsy(int);
double pam(int);
void estimate(int lines, double(*pf)(int));
int main()
{
using namespace std;
int code;
cout << "how many code of lines do you need" << endl;
cin >> code;
cout << "here's betsy's estimate" << endl;
estimate(code, betsy);
cout << "here's pam's estimate" << endl;
estimate(code,pam);
system("pause");
return 0;
}
double betsy(int code)
{
return 0.05*code;
}
double pam(int code)
{
return 0.03*code + 0.0004*code*code;
}
void estimate(int code,double (*pf)(int))
{
using namespace std;
cout << code << " lines will take";
//cout << (*pf)(code) << "hour(s)\n";
cout << pf(code) << "hour(s)\n";
}
声明两个返回值和函数特征标(函数参数)相同的函数,然后通过传入不同的函数指针,分别调用。
函数指针数组及auto关键字在此处的应用
#include
const double * f1(const double ar[], int n);
const double * f2(const double[], int n);
const double * f3(const double * ,int n);
int main()
{
using namespace std;
double av[3] = {1112.3,1542.6,2227.9};
const double *(*p1)(const double *, int n) = f1;
auto p2 = f2;
//pre-c++11 can use the following code instead
//const double * (*p2)(const double *,int n)=f2;
cout << "using pointers to function\n";
cout << "Address value\n";
cout << (*p1)(av, 3) << ": " << *(*p1)(av, 3) << endl;
cout << (*p2)(av, 3) << ": " << *(*p2)(av, 3) << endl;
//pa an array of pointers
//auto doesn't work with list initialization
const double *(*pa[3])(const double *, int n) = {f1,f2,f3};
auto pb = pa;
//pre-c++11 can use the following code instead
//const double *(**pb)(double const * ,int n)=pa;
cout << "\nusing an array of pointers to function:\n ";
cout << "Address value\n";
for (int i = 0; i < 3; i++)
cout << pa[i](av, 3) << ": " << *pa[i](av, 3) << endl;
cout << "\n using a pointer to apointer to a function\n";
cout << "Address value\n";
for (int i = 0; i < 3; i++)
cout << *(pb[i])(av, 3) << ": " << *(*pb[i])(av, 3) << endl;
cout << "\nusing a pointer to an array of function pointers\n";
cout << "Address value" << endl;
auto pc = &pa;
//pre-c++11 can use the following code instead
//const double *(*(*pc)[3])(const double *,int n)=&pa;
cout << (*pc)[0] << ": " << *(*pc)[0](av, 3) << endl;
const double *(*(*pd)[3])(const double *, int n) = &pa;
cout << (*pd)[1]<< ": " << *(*pd)[1](av, 3) << endl;
cout << (*pd)[2] << ": " << *(*pd)[2](av, 3) << endl;
system("pause");
return 0;
}
在这个例子中要注意以下几点:
auto不能用来初始化列表,所以在声明pa时没有用auto。
其实整个例子关键还是在与数组指针与指针数组,只不过现在的指针指向的数组存放的是函数指针。
pa与&pa的值相同,但含义不同,pa是数组第一个元素pa[0]的地址,即&pa[0]。而&pa是整个数组的起始地址。所以pa+1指向的是pa[1],而&pa+1指向的是数组pa后面12个内存地址块的地址(假设指针为4个字节)。
使用auto能够简化很多事情,还防止出错。
6、typedef用途与陷阱
用途一:
定义一种类型的别名,而不是简单的替换
char *pa,pb;
typedef char * PCHAR;
PCHAR pc,pd;
在上述代码中,pb并不是指针类型,其余都是。
用途二:
在C中声明struct新对象时,必须带上typedef。可以通过下面的方法少写struct。
typedef struct tagPoint
{
int x;
int y;
}Point,*PPoint;
用途三:
用typedef声明与平台无关的类型
如定义一个叫REAL的浮点类型,在目标平台一上,他表示的最高精度类型为:
typedef long double REAL;
在不支持long double类型的平台二上,改为:
typedef double REAL
也就是说,跨平台时,只需要改REAL就行了。
用途四:
为复杂的声明定义一个简单的别名。
const double * f1(const double ar[], int n);
const double * f2(const double[], int n);
const double * f3(const double * ,int n);
typedef const double *(*pe[3])(const double *,int );
pe pp={f1,f2,f3};
pe是个新类型的别名,新类型是个3个元素的数组,元素类型是函数指针。
陷阱一:
typedef是定义了一种类型的新别名,而不是简单的替换。
typedef char * pstr;
char aaa[5] = "hell";
const pstr p = aaa;
p++;
p++报错,因为const修饰的是整个指针,而不是char,所以p实际上是一个指针常量,不允许改变指向的地址。
陷阱二:
虽然typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef static int INT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。