関数ポインタとポインタ関数の学習のまとめ


関数のポインタは関数のポインタを指しています。ポインタ関数は関数の戻り値を指しています。詳しく教えてもらえますか?1)float(**def)[10]   defとは何ですか2)double*(*gh)[10]   ghとは何ですか3)ダブル(*f[10])()   fは何ですか4)*((**b)[10])    bは何ですか?このようにいつも感じが乱れていますが、覚えて理解できるコツがありますか?
==================================解答:   (1)defは、ポインタであり、対象も一つのポインタであり、最終的には10個のfloatからなる配列を指す。
(2)ghはポインタで、10個の要素からなる配列を指します。配列の要素はdouble*タイプのポインタです。
(3)fは10要素からなる配列であり、各要素はポインタであり、ポインタは関数を指し、関数の種類はパラメータがなく、戻り値はdoubleである。以下で説明するコツの例はこれと類似している。
(4)bはポインタで、10個の要素からなる配列を指し、配列要素はint*型のポインタです。
コツは以下の通りです。複雑なタイプの声明に会ったら、どうやって解析しますか?例えば、char(*a[3])(int)a一体なぜ声明されていますか?ポインタ?配列?それとも関数ですか解析は、aが一番近い(演算子優先度)から開始します。私たちはaが一番近い記号を見ました。*は[]より低いです。a後に[]がある以上、aは配列であり、3つの要素を含む配列である。この配列の各要素はどのタイプですか?配列aはa[0]、a[1]、a[2]の3つの要素だけを含んでいますが、a[3]は実際には境界を越えています。しかし、配列aの要素の種類を分析するには、形式的な要素a[3]が必要です。a[3]のタイプがわかったら、aの元素のタイプが分かります。a[3]はどんなタイプですか?針であり、その前に*があるので、配列aの要素はポインタであることが分かります。針だけでは足りない。針に対しては、指し示すものはどのようなタイプですか?何を指していますか?a[3]は何ですか?優先度で続けてみます。*a[3]の後ろには小さい括弧があります。だから、*a[3]は関数であることは間違いありません。つまり配列aの要素は関数を指すポインタです。どんな種類の関数を指していますか?これは明らかであり、パラメータがintであり、戻り値がcharのタイプの関数である。これで解析済みです。上記の方法によって、複雑でも一歩ずつ解決できます。
武芸は人を殴るためではなく、身を守るためであるように、上記の方法を理解するために、他人の複雑な声明を読むためであり、実践の中で自分でこのような複雑なものを作るためではない。複雑な声明が必要な場合は、一部をtypedefで代えることができます。例えば、上の文は2つに変えられます。typedef char(*FUN_)PTR)(int)ふうふPTR a[3];これではっきりしました。また、上記の分析方法は、いくつかのものの本質をより明確にします。例えば、n次元配列の本質は一次元配列である。具体例を見ると、int a[3][5];この声明は3つの要素を含む1次元配列であり、各要素はまた5つのint数からなる配列である。aは5つの要素を含む1次元配列であり、各要素は3つのint数からなる配列であると理解できない。なぜですか?やはり上の方法で分析します。ここは省略します。
本やネット上で「右を見て左を見る」という方法を提供しているものがありますが、実際には汎用性に欠けるものがあります。例えば多次元配列の本質を分析するのには適していません。そしてこの方法は本質を隠しています。  ==============================================================================  一、ポインタ関数は、関数によって、その戻り値がポインタであると宣言された場合、実際には、ポインタまたはアドレスを必要とする表式にアドレスを返します。フォーマット:タイプの引数*関数名(パラメータ)はもちろん、戻りはアドレスですので、タイプの引数は常にintです。例えば、int*GetDate();      int*aa(int,int)関数が返したのはアドレス値で、戻り行列の要素アドレスによく使われます。

        int * GetDate(int wk,int dy);
        main()
        {
            int wk,dy;
            do
            {
                printf(Enter week(1-5)day(1-7)/n);
                scanf(%d%d,&wk,&dy);
            }
            while(wk<1||wk>5||dy<1||dy>7);
            printf(%d/n,*GetDate(wk,dy));
        }

        int * GetDate(int wk,int dy)
        {
            static int calendar[5][7]=
            {
               {1,2,3,4,5,6,7},
               {8,9,10,11,12,13,14},
               {15,16,17,18,19,20,21},
               {22,23,24,25,26,27,28},
               {29,30,31,-1}
            };
            return &calendar[wk-1][dy-1];
        }

        プログラムはよく理解されているはずです。サブ関数はある要素の配列のアドレスを返します。出力するのはこの住所の値です。
二、関数のポインタは関数のアドレスを指しています。これによって関数を呼び出すことができます。宣言形式は以下の通りです。タイプの引数(*関数名)(パラメータ)はここでは関数名とは言えません。ポインタの変数名と呼ぶべきです。この特殊なポインタは、整数値を返す関数を指します。ポインターの声明は関数に向けられた声明と一致しなければならない。ポインタ名とポインタ演算子の外側の括弧は、デフォルトの演算子優先度を変更します。括弧がない場合は、全体のポインタを返す関数のプロトタイプ宣言になります。たとえば:    void(*fptr)();関数のアドレスを関数ポインタに割り当てます。次の2つの形が使えます。        fptr=&Function;        fptr=Function;アドレス演算子の取得&必要ではありません。関数識別子だけで、そのアドレスが記号で表されています。関数呼び出しであれば、丸括弧で囲まれたパラメータテーブルも含まれていなければなりません。関数をポインタで呼び出すには、次の2つの方法があります。        x=(*fptr)()        x=fptr();第二のフォーマットは、見た目と関数呼び出しと同じです。しかし、いくつかのプログラマは、関数ではなくポインタによって関数を呼び出すことを明確に指摘するため、最初のフォーマットを使用する傾向があります。次の例を挙げます。

        void (*funcp)();
        void FileFunc(),EditFunc();
        main()
        {
            funcp=FileFunc;
            (*funcp)();
            funcp=EditFunc;
            (*funcp)();
        }

        void FileFunc()
        {
            printf("FileFunc/n");
        }

        void EditFunc()
        {
            printf("EditFunc/n");
        }

プログラム出力は以下の通りです。    FileFun    EditFun
三、針の針の針はちょっと分かりにくいです。それらの声明には二つの星があります。たとえば:        char****cp;三つの星があれば、針の針です。四つの星は、針の針の針です。順番に類推します。簡単な例に慣れたら、複雑な状況に対処できます。もちろん、実際のプログラムでは、普通は二級の針しか使わないです。三つの星はあまり見られないです。まして、四つの星号はなおさらです。ポインタの針は、ポインタの住所が必要です。        char c='A';        char*p=&c;        char****cp=&p;ポインタを通して、ポインタだけでなく、ポインタが指すデータにもアクセスできます。以下はいくつかのこのような例です。        char*p 1=*cp;   // (&c)        char c 1=**cp;このような構造が何の役に立つか知りたいかもしれません。ポインタのポインタを使用して、局所ポインタ変数および処理ポインタ配列を、呼び出し関数によって変更することができます。

        void FindCredit(int **);
        main()
        {
            int vals[]={7,6,5,-4,3,2,1,0};
            int *fp=vals;
            FindCredit(&fp);
            printf(%d/n,*fp);
        }

        void FindCredit(int ** fpp)
        {
            while(**fpp!=0)
            if(**fpp<0) break;
            else (*fpp)++;
        }

は、最初に1つの配列のアドレスでポインタfpを初期化し、その後、ポインタのアドレスを実際参照として関数FindCredit()に渡す。FindCredit()関数は,行列内のデータを式*fppで間接的に得た。行列を巡回して負の値を見つけるために、FindCredit()関数の自己増加演算の対象は、調合者の配列を指すポインタであり、それ自体が調合者ポインタを指すポインタではない。語句(*fpp)++は、形の指針を自己加算します。ただし、演算子が+演算子より高いので、丸括弧はここで必要です。丸括弧がないと、++演算子は二重ポインタfppに作用します。
四、ポインタ配列を指すポインタの他の用法として旧処理ポインタ配列があります。いくつかのプログラマは、多次元配列の代わりにポインタ配列を使用することが好きで、よくある使い方は文字列を処理することです。

        char *Names[]=
        {
             Bill,
             Sam,
             Jim,
             Paul,
             Charles,
             0
        };

        main()
        {
            char **nm=Names;
            while(*nm!=0) printf(%s/n,*nm++);
        }

は、まず文字型ポインタ配列Namesのアドレスでポインタnmを初期化する。各printf()の呼び出しは、まずポインタnmが指す文字型ポインタを伝達し、その後、nmを自己増加演算して配列の次の要素(またはポインタ)を指す。上記のように考えられている文法は*nm++であり、まず指針の内容を取得してから、ポインタを自然に増加させます。注意配列の最後の要素は0に初期化され、whileは配列の最後に来たかどうかを判定するためにループします。ゼロ値を持つポインタは、しばしば循環配列の終端符として使用される。プログラマは、ゼロ値ポインタを空のポインタ(NULL)と呼びます。空のポインタを終端符として使用します。ツリーの削除時には、巡回配列のコードを変更する必要はありません。このとき、配列は空のポインタで終了します。