c++拾遗-----函数

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; //不可行

编译将失败,会提示“指定了一个以上的存储类”。

你可能感兴趣的:(c++拾遗,c++,函数指针,typedef)