指標和函數

複雜宣告的讀法

文法,最令我印象深刻的,莫過於印度工程師Vikram的"The right-left rule"。他是這麼說的:
「從最內層的括號讀起,變數名稱,然後往右,遇到括號就往左。當括號內的東西都解讀完畢了,就跳出括號繼續未完成的部份,重覆上面的步驟直到解讀完畢。」
舉個例子:void ** (*d) (int &, char*)依下面方式解讀:
1. 最內層括號的讀起,變數名稱: d
2. 往右直到碰到) : (空白)
3. 往左直到碰到( :是一個函數指標
4. 跳出括號,往右,碰到(int &, char*): 此函式接受兩個參數:第一個參數是reference to integer,第二個參數是character pointer。
5. 往左遇上void **: 此函式回傳的型態為pointer to pointer to void。
==> d是一個函式指標,指向的函式接受int&和char*兩個參數並回傳void**的型態。
如何,是不是好懂很多了呢?

標題中的void ** (*d) (int &, char **(*)(char *, char **))其實和上面的例子幾乎一樣,只是函式的第二個參數又是一個函式指標,接受char*和char**兩個參數並回傳char**的型態。

 

---------

下面結合例子來演示一下「右左法則」的使用。

int * (* (*fp1) (int) ) [10];


閱讀步驟:
1. 從變數名開始 -------------------------------------------- fp1
2. 往右看,什麼也沒有,碰到了),因此往左看,碰到一個* ------ 一個指標
3. 跳出括弧,碰到了(int) ----------------------------------- 一個帶一個int參數的函數
4. 向左看,發現一個* --------------------------------------- (函數)返回一個指標
5. 跳出括弧,向右看,碰到[10] ------------------------------ 一個10元素的陣列
6. 向左看,發現一個* --------------------------------------- 指標
7. 向左看,發現int ----------------------------------------- int類型

總結:fp1被聲明成為一個函數的指標,該函數返回指向指標陣列的指標.

再來看一個例子:

int *( *( *arr[5])())();


閱讀步驟:
1. 從變數名開始 -------------------------------------------- arr
2. 往右看,發現是一個陣列 ---------------------------------- 一個5元素的陣列
3. 向左看,發現一個* --------------------------------------- 指標
4. 跳出括弧,向右看,發現() -------------------------------- 不帶參數的函數
5. 向左看,碰到* ------------------------------------------- (函數)返回一個指標
6. 跳出括弧,向右發現() ------------------------------------ 不帶參數的函數
7. 向左,發現* --------------------------------------------- (函數)返回一個指標
8. 繼續向左,發現int --------------------------------------- int類型

還有更多的例子:

float ( * ( *b()) [] )(); // b is a function that returns a
// pointer to an array of pointers
// to functions returning floats.

void * ( *c) ( char, int (*)()); // c is a pointer to a function that takes
// two parameters:
// a char and a pointer to a
// function that takes no
// parameters and returns
// an int
// and returns a pointer to void.

void ** (*d) (int &,
char **(*)(char *, char **)); // d is a pointer to a function that takes
// two parameters:
// a reference to an int and a pointer
// to a function that takes two parameters:
// a pointer to a char and a pointer
// to a pointer to a char
// and returns a pointer to a pointer
// to a char
// and returns a pointer to a pointer to void

float ( * ( * e[10])
(int &) ) [5]; // e is an array of 10 pointers to
// functions that take a single
// reference to an int as an argument
// and return pointers to
// an array of 5 floats.

---------

下面的宣告摘錄自"The C Programming Language"第二版的第122頁,請讀者寫出其宣告意義

char **argv;            argv : pointer to pointer to char       
int (*daytab)[13];      daytab : pointer to array[13] of int   
int *daytab[13];        daytab : array[13] of pointer to int
void *comp();           comp : function returning pointer to void
void (*comp)();         comp : pointer to function returning void
char (*(*x())[])();     x : function returning pointer to array[] of pointer to function returning char
char (*(*x[3])())[5];   

x : array[3] of pointer to function returning pointer to array[5] of char

口訣

  • 看見[]就說array[] of
  • 看見*就說pointer to
  • 看見變數後面的()就說function() returning

以下是計算積分的程式範例,用到pointer to function的觀念

#include <stdio.h>
#include <math.h>

/*
 * 計算平方
 */
double square(double x) {
    return x * x;
}

/*
 * 計算三次方
 */
double cube(double x) {
    return x * x * x;
}

/*
 * 計算f()在(x,y)之間以n等份來逼近的積分數值,使用梯形法
 */
double integral(double (*f)(double), int n, double x, double y) {
    int i;
    double gap = (y - x) / n;
    double fy1 = (*f)(x);
    double fy2 = (*f)(x + gap);
    double area = 0;
    for (i = 0; i < n; i++) {
        area += (fy1 + fy2) * gap / 2; // 使用梯形面積公式 
        fy1 = fy2;
        fy2 = (*f)(x + gap * (i + 1)); //下底
    }
    return area;
}

int main() {
    char fun[100];
    int n;
    double x, y;
    double (*f)(double); // f: a pointer to function(double) returning double
    while (scanf("%99s",fun) != EOF) { // EOF定義於stdio.h內,一般系統上為-1
        if (strcmp(fun,"square")==0) {
            f = square;
        } else if (strcmp(fun,"cube")==0) {
            f = cube;
        } else if (strcmp(fun,"sqrt")==0) {
            f = sqrt; // sqrt is defined in math.h
        } else if (strcmp(fun,"cbrt")==0) {
            f = cbrt; // cbrt is defined in math.h
        } else if (strcmp(fun,"end")==0) {
            break;
        } else {
            printf("Unknown function\n");
            continue;
        }
        scanf("%d%lf%lf", &n, &x, &y);
        printf("Integral of %s from %lf to %lf is: %lf\n", fun, x, y, integral(f, n, x, y));
    }
    return 0;
}

如果要讓積分算得更快的話,integral也可改寫如下

double integral(double (*f)(double), int n, double x, double y) {
    int i;
    double area = ((*f)(x) + (*f)(y)) / 2.0L;
    double gap = (y - x) / n;
    double next = x;
    for (i = 1; i < n; i++) {
        area += (*f)(next += gap);
    }
    return area * gap;
}

上面寫法會比較快的精神在於,讓迴圈內的東西越簡單越好,因為迴圈通常是程式花最多時間的地方。

posted @ 2013-06-12 06:13  jeremyatchina  阅读(281)  评论(0编辑  收藏  举报