zoukankan      html  css  js  c++  java
  • 指標和函數

    複雜宣告的讀法

    文法,最令我印象深刻的,莫過於印度工程師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;
    }
    

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

  • 相关阅读:
    js上传超大文件解决方案
    java上传超大文件解决方案
    jsp上传超大文件解决方案
    .net上传超大文件解决方案
    asp.net上传超大文件解决方案
    PHP上传超大文件解决方案
    内网大文件传输断点续传源码
    HDU
    Android中makfile的随记
    android 阿拉伯语下,图库中编辑运动轨迹图片,动画中会显示绿色的图片
  • 原文地址:https://www.cnblogs.com/bittorrent/p/3132403.html
Copyright © 2011-2022 走看看