書面フォーマット解惑編


//////////////////////////////////////////////////////////////////////////////////////////通常の関数呼び出し通常の関数呼び出しの例:
//       
void MyFun(int x); //         :void MyFun( int );

int main(int argc, char* argv[])
{
   MyFun(10); //     MyFun(10);  

      return 0;
}

void MyFun(int x) //      MyFun  
{
   printf(“%d
”,x); }
  MyFun            ,         。                  !       MyFun       :

MyFun(10); 私たちは最初は機能的または数学的な意味でMyFunという関数を理解しただけで、MyFun関数名が機能(またはコード)を表していることを知っています.--関数ポインタの概念を学ぶまで、関数名はいったい何なのか考えざるを得ませんでした.(意味のないことだと思わないでください.ほほほ、下を見ればわかります.)
二関数ポインタ変数の申明は,あるデータ変数のメモリアドレスが対応するポインタ変数に格納できるように,関数のヘッダアドレスもある関数ポインタ変数に格納される.これにより、この関数ポインタ変数で指向する関数を呼び出すことができます.Cシリーズ言語では、いずれの変数も、必ず先に説明してから使用します.では、関数ポインタ変数も先に明らかにすべきではないでしょうか.それはどうやって明らかにしますか?上記の例を例として,MyFun関数を指すことができる関数ポインタ変数FunPについて説明する.次にFunP変数を説明する方法を示します.
void (*FunP)(int) ;   //    void (*FunP)(int x);

関数ポインタ変数全体の宣言フォーマットは、関数MyFunの宣言と同じですが、MyFunを(*FunP)に変更しただけで、MyFun関数を指すポインタFunPがあります.(もちろん、このFunPポインタ変数は、同じパラメータと戻り値を持つ他の関数を指すこともできます.)
三関数ポインタ変数呼び出し関数にFunPポインタ変数があれば、MyFunを指し示す値を付け、FunPでMyFun関数を呼び出すことができます.FunPポインタ変数でMyFun関数を呼び出す方法を見てみましょう.
//       
void MyFun(int x); //        :void MyFun( int );
void (*FunP)(int ); //     void(*FunP)(int x),         。

int main(int argc, char* argv[])
{
   MyFun(10); //      MyFun  
   FunP=&MyFun; // MyFun       FunP  
   (*FunP)(20); //          FunP   MyFun   。
}

void MyFun(int x) //      MyFun  
{
   printf(“%d
”,x); } 。 。 , , 。 , :MyFun FunP int int * 。 MyFun int ( ), FunP int * 。 int i,*pi; pi=&i; // FunP=&MyFun 。 ( ?) , ——

四呼び出し関数の他の書式関数ポインタも、次のように使用して、同じことを実行できます.
//       
void MyFun(int x);
void (*FunP)(int ); //            ,          。
int main(int argc, char* argv[])
{
   MyFun(10); //     MyFun(10);  
   FunP=MyFun; // MyFun       FunP  
   FunP(20); //             MyFun   。

      return 0;
}

void MyFun(int x) //      MyFun  
{
   printf(“%d
”,x); }

私は黒字部分を変更しました(自分で前のコードと比較してください).実行してみて、あ!同じように成功しました.あれ?
FunP=MyFun;

このようにMyFun値をFunPに割り当てることができます.まさかMyFunとFunPは同じデータ型(つまりintとintの関係)で、intとint*の関係ではありませんか?(少しぼんやりしていませんか?)前のコードと少し矛盾しているようですね!だから私は言います!しばらく説明しないで、以下の状況を見続けてください(これらはすべて正しく実行できるコードですよ!):
int main(int argc, char* argv[])
{
   MyFun(10); //     MyFun(10);  
   FunP=&MyFun; // MyFun       FunP  
   FunP(20); //             MyFun   。

      return 0;
}
    :
int main(int argc, char* argv[])
{
   MyFun(10); //     MyFun(10);  
   FunP=MyFun; // MyFun       FunP  
   (*FunP)(20); //             MyFun   。

      return 0;
}
         !
( !      !)
   ! ——
int main(int argc, char* argv[])
{
   (*MyFun)(10); // ,   MyFun           

      return 0;
}

初めて見たかもしれませんが、関数名呼び出しもこのように書くことができます.(ただ、私たちは普段そう書いていないだけです.)では、これらは何を説明していますか?ほほほ!もし私が「ホームズ」だったら、これまでの知識と経験に基づいて本編の「新しい発見」を推理します.これによって、必ず以下の結論を分析し、推定する.実は、MyFunの関数名はFunPの関数ポインタと同じです.つまり、関数ポインタです.MyFun関数名は関数ポインタ定数であり、FunPは関数数ポインタ変数であり、これらの関係である.2.ただし、関数名呼び出しがすべて(*MyFun)(10)の場合.このように、その書くことも読むことも不便で慣れていない.だからC言語の設計者たちはMyFun(10)を許可するように設計された.3.統一のため、FunP関数ポインタ変数もFunP(10)の形式で呼び出すことができます.4.付与する場合、FunP=&MyFun形式でもFunP=MyFun形式でもよい.上記のコードの書き方は、好きなようにしてください.そう理解してください.これは関数ポインタの応用に役立ちますね.最後に、関数の説明について補足します.
void MyFun(int );    //    void (*MyFun)(int )。
void (*FunP)(int );   //    void FunP(int )。

(注釈を参照)この点は注意しなければならない.
5関数のポインタタイプを定義します.カスタムデータ型のように、関数ポインタタイプを定義してから、このタイプで関数ポインタ変数を説明することもできます.まず、カスタムデータ型の例をあげます.
typedef int* PINT; // int*        PINT   
int main()
{
  int x;
  PINT px=&x; // int * px=&x;    。PINT      int *   
  *px=10; //px  int*     
  return 0;
}

注釈によると、難しくはないでしょう.(このような定義は少ないかもしれませんが、後でWin 32のプログラミングを学ぶときによく見られます.)関数ポインタタイプの定義と使用を見てみましょう:(上と対照してください!)/自分でヘッダファイルを含めてください.
void MyFun(int x); //         :void MyFun( int );
typedef void (*FunType)(int ); //              
FunType FunP; //   FunType       FunP  

int main(int argc, char* argv[])
{
//FunType FunP; //                 ,         。
   MyFun(10);
   FunP=&MyFun;
   (*FunP)(20);

      return 0;
}

void MyFun(int x)
{
   printf(“%d
”,x); }

黒体部分を見る:まず、void(*FunType)(int);前にtypedefを追加しました.これは、FunType変数ではなくFunType関数というポインタタイプを定義するだけです.そして、FunType FunP;この文はPINT pxのようです.同じようにFunP変数を明らかにします.その他は同じです.プログラム全体が同じことを完成した.このようなアプローチの利点は,FunTypeタイプがあれば,同じタイプの複数の関数ポインタ変数を同様に容易にFunTypeタイプで明示できることである.次のようになります.
FunType FunP2;
FunType FunP3;

//……
六関数ポインタは、ある関数のパラメータとして、関数ポインタ変数が変数である以上、ある関数のパラメータとして使用することもできる.したがって、関数ポインタがどのように関数のパラメータとして使用されているかを知る必要があります.例をあげます:要求:私は1つのCallMyFun関数を設計して、この関数はパラメータの中の関数のポインタの値を通じてそれぞれMyFun 1、MyFun 2、MyFun 3の3つの関数を呼び出すことができます(注:この3つの関数の定義のフォーマットは同じです).実現:コードは以下の通りです:
//       
void MyFun1(int x);
void MyFun2(int x);
void MyFun3(int x);
typedef void (*FunType)(int ); //②.           FunType, ①      
void CallMyFun(FunType fp,int x);

int main(int argc, char* argv[])
{
   CallMyFun(MyFun1,10); //⑤.   CallMyFun             
   CallMyFun(MyFun2,20);
   CallMyFun(MyFun3,30);
}
void CallMyFun(FunType fp,int x) //③.   fp    FunType。
{
  fp(x);//④.   fp            ,  fp            
}
void MyFun1(int x) // ①.            ,         
{
   printf(“  MyFun1   :%d
”,x); } void MyFun2(int x) { printf(“ MyFun2 :%d
”,x); } void MyFun3(int x) { printf(“ MyFun3 :%d
”,x); } :

分析:(私が書いた注釈を見てください.あなたは私の注釈の①②③④の順番で自分で分析することができます.)
以上の部分は転載ネットユーザーが述べたものです.原文の住所は次のとおりです.http://blog.pfan.cn/whyhappy/6030.html
7アドレスジャンプ
void(*reset)(void)= (void(*)(void))0

void(reset)(void)は関数ポインタ定義であり、(void()(void))0は強制タイプ変換操作であり、数値「0」を関数ポインタアドレス「0」に強制変換する.reset()関数を呼び出すと、プログラムはプログラム実行の「0」アドレスにジャンプして再実行されます.Nboot、UBOot、EBootなどの他の高度なシングルチップマシンBootloaderでは、これらのBootloaderによってダウンロードプログラムが頻繁に実行され、関数ポインタによって実行するプログラムのアドレスにジャンプします.
void (*theUboot)(void); 。。。。 theUboot = (void (*)(void))(0x30700000);
    theUboot();
    。。。。。
 (*(void (*)(void))(0x30700000))();

強制型変換、絶対アドレスを関数ポインタに変換し、前に述べた絶対アドレスにジャンプするためにこの関数を呼び出します.
mov r0,0x30700000;
mov pc,r0

((void()(void)(0 x 307000000))();まず(void(*)(void))は強制型変換子であり、後の0 x 307000万という符号なし整数を強制的に関数ポインタに変換し、この関数ポインタが指す関数エントリパラメータはvoidであり、戻り値もvoidであることが理解できる.ここまで読めば、(void(*)(void)(0 x 307000000)をfpに設定します.では、上記の式は(*fp)();OK、これでわかりました.上で変換した関数ポインタを参照します(つまり、関数ポインタが指す関数を呼び出します).