C言語のあれらの小さい秘密の【関数の指針】

7741 ワード

FROM: http://blog.csdn.net/bigloomy/article/details/6584858
================================================
私たちはよくこのような言い方を耳にしますが、関数のポインタを知らないのは本当のC言語の達人ではありません.私たちはこの言葉が正しいかどうかにかかわらず、側面から関数ポインタの重要性を反映しているので、関数ポインタの使用を把握する必要があります.まず関数ポインタの定義を見てみましょう.
関数は実行文からなる命令シーケンスまたはコードであり、これらのコードの秩序集合はその大きさに応じて一定のメモリ空間に割り当てられ、このメモリ空間の開始アドレスは関数のアドレスとなり、異なる関数には異なる関数アドレスがあり、コンパイラは関数名によって関数のエントリアドレスをインデックスし、操作タイプの属性が同じ関数を容易にするために,c/c++は関数ポインタを導入し,関数ポインタはコードエントリアドレスを指すポインタであり,関数を指すポインタ変数である.したがって、「関数ポインタ」自体は、まずポインタ変数であるべきですが、このポインタ変数は関数を指しています.これは、ポインタ変数で整形変数、文字型、配列を指すことができるように、ここでは関数を指す.Cコンパイル時、各関数には、関数ポインタが指すアドレスであるエントリアドレスがある.関数を指すポインタ変数があれば、ポインタ変数で他のタイプの変数を参照できるように、このポインタ変数で関数を呼び出すことができます.これらの概念は一致しています.関数ポインタには、関数の呼び出しと関数のパラメータの2つの用途があります.
関数ポインタの宣言方法は次のとおりです.
データ型フラグ(ポインタ変数名)(パラメータリスト);
「()」の優先度が「*」より高いため、ポインタ変数名の外側の括弧は欠かせません.後の「パラメータリスト」は、ポインタ変数が指す関数のパラメータリストを表します.例:
  int function(int x,int y);/* 関数を宣言*/
  int (*f) (int x,int y);/* 関数ポインタを宣言*/
  f=function;/* function関数のヘッダアドレスをポインタf*/に割り当てる
付与時の関数functionは括弧もパラメータも持たず、functionは関数のヘッダアドレスを表すため、付与後、ポインタfは関数function(int x,int y)を指す.のコードの先頭アドレスです.
次のプログラムでは、関数ポインタが関数を呼び出す方法を説明します.
例一、
 
 #include<stdio.h> 
     int max ( int x, int y){ return x>y?x:y;}
     int min ( int x, int y){ return x<y?x:y;}
     void main ()
     { int ( *f ) ( int x, int y)=max;  
     //f=&max; 
     printf ( "%d,%d\t", max (2,6), (f)(5,4));
     f=min;                   
     printf (" %d,%d\t" , min (2,6), (f)(5,4)); 
}  

注意:以上のコードの赤い部分は次のコード分析部分で説明します.読者もコメント部分を実行すれば、結果は正しいのではないかと考えることができます.
fは関数を指すポインタ変数なので、関数max()をfの値として与えることができます.すなわち、max()のエントリアドレスをfに割り当てることができます.後でfでこの関数を呼び出すことができます.実際には、fとmaxは同じエントリアドレスを指しています.違いは、fはポインタ変数で、関数名のように死んでいません.それはどんな関数を指すことができます.どうすればいいかによって決まります.プログラムでどの関数のアドレスを割り当てるかは、どの関数を指します.その後、ポインタ変数で呼び出すため、異なる関数を前後して指すことができます.ただし、関数を指すポインタ変数には++と--の演算はありませんので、注意してください.
関数カッコ内のパラメータは、状況に応じて異なりますが、一部のコンパイラでは通過できません.この例の補足は以下の通りです.
  1.関数ポインタのタイプを定義します.
  typedef int (*fun_ptr)(int,int);
  2.変数を明示し、値を付与します.
  fun_ptr max_func=max;
すなわち,関数ポインタに付与される関数は,関数ポインタが指す関数プロトタイプと一致するはずである.
例二、
  
#include<stdio.h>
  void FileFunc()
  {
  printf("FileFunc
");   }   void EditFunc()   {   printf("EditFunc
");   }   void main()   {   typedef void (*funcp)();   funcp pfun= FileFunc;   pfun();   pfun = EditFunc;   pfun();    }

上記の2つのコードを見て、関数ポインタで関数を呼び出す方法を知っているはずですが、コメント部分f=&maxを実行するという問題を上記の説明に残しました.結果は正しいのでしょうか.次に、上記の2つの実行結果のペアを示し、原因を分析します.
注記前の実行結果:C语言的那些小秘密之【函数指针】
コメント部分を追加した実行結果は次のとおりです.
C语言的那些小秘密之【函数指针】
以上の実行結果を比較すると,f=&max文が実行された場合の結果と実行されなかった場合の結果は同じである.どうしてこんな結果になったのでしょうか.答えはこれがコンパイラが処理したもので、max自体はアドレスで、それはいかなる変数にも入っていないので、自然にアドレスを取っていません.デバッグ中に&maxの値とmaxの値が同じであることを見ることができます.デバッグコードは次のとおりです.
root@ubuntu:/home/shiyan # gdb ss GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2 Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later < http://gnu.org/licenses/gpl.html > This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  Type "show copying"and "show warranty"for details. This GDB was configured as "i686-linux-gnu". For bug reporting instructions, please see: < http://www.gnu.org/software/gdb/bugs/ >... Reading symbols from/home/shiyan/ss...done. (gdb) list 1  #include  2  int max ( int x, int y){ return x>y?x:y;} 3  int min ( int x, int y){ return xBreakpoint 1, main () at hanshu.c:5 5  { int ( *f ) ( int x, int y)=max;   (gdb) print max $1 = {int (int, int)} 0x80483c4 (gdb) print f $2 = (int (*)(int, int)) 0xbffff6c8 (gdb) s 7  printf ( "%d,%d\t", max (2,6), (f)(5,4)); (gdb)  max (x=5, y=4) at hanshu.c:2 2  int max ( int x, int y){ return x>y?x:y;} (gdb) print max $3 = {int (int, int)} 0x80483c4 (gdb) print &max $4 = (int (*)(int, int)) 0x80483c4 (gdb) print *max $5 = {int (int, int)} 0x80483c4 (gdb) s max (x=2, y=6) at hanshu.c:2 2  int max ( int x, int y){ return x>y?x:y;} (gdb) s main () at hanshu.c:8 8  f=min;                    (gdb) print min $6 = {int (int, int)} 0x80483d3 (gdb) print &min $7 = (int (*)(int, int)) 0x80483d3 (gdb) print *min $8 = {int (int, int)} 0x80483d3 (gdb) s 9  printf ("%d,%d\t", min (2,6), (f)(5,4));  (gdb) print f $9 = (int (*)(int, int)) 0x80483d3 (gdb) print &f $10 = (int (**)(int, int)) 0xbffff6ac (gdb) print *f $11 = {int (int, int)} 0x80483d3 (gdb) s min (x=5, y=4) at hanshu.c:3 3  int min ( int x, int y){ return x (gdb) s main () at hanshu.c:10 10 }
デバッグの過程で私は多くの情報をprintして、細心の読者はきっともっと多くの収穫を得ることができて、特に変数fのprintに対して、読者は自分で読むことができて、もっと多くのものを学ぶことができます.私が提供したのはただ1つの参考のデバッグ方式で、読者が1対3を挙げて、自分でコードに対して実際のデバッグを行って、理解を深めることを望んでいます.
上記はポインタで関数の呼び出しを実現していますが、次に関数ポインタをパラメータとして使用する方法を見てみましょう.
#include <iostream>
using namespace std;
typedef int (*print)(int );
int fun1(int i)
{  
          return (int)i;  
}  
   
void fun2(int j,print prt)
{  
 for(int k=0;k<j;k++)
    cout<<'\t'<<prt(k)<<'
'; } void main() { int i=10; fun2(i,fun1); }

実行結果は次のとおりです.
  C语言的那些小秘密之【函数指针】
上の説明を見て、私はすべて関数のポインタの概念に対して大体の理解があると思って、もう一つはみんなが混同しないことを望んでいる概念はポインタの関数で、この2つの概念はすべて略称で、ポインタの関数はポインタの関数を指して、つまり本質は1つの関数です.関数には戻りタイプがあることは知っていますが(値を返さない場合は無値タイプ、すなわちvoid)、ポインタ関数の戻りタイプはあるタイプのポインタです.
定義されたフォーマットは次のとおりです.
戻りタイプ識別子*戻り名(形式パラメータテーブル)
{関数体}
戻りタイプは、任意の基本タイプと複合タイプです.ポインタを返す関数の用途は非常に広い.実際、各関数には、あるタイプのポインタを返さなくても、ポインタに相当するエントリアドレスがあります.たとえば、関数が整数値を返すのは、実際にはポインタ変数の値を返すことにも相当しますが、この場合の変数は関数そのものであり、関数全体が「変数」に相当します.関数の戻り値については、次の章で説明します.以上の内容があなたに役に立つことを望んでいます!
C言語は博大で奥深く、本人のレベルが限られているため、ブログの中の不適切さや間違いは避けられない.読者の批判と指摘を切に望んでいる.また、読者の皆さんが関連内容を検討することを歓迎します.喜んで交流すれば、貴重な意見を残してください.  ====================================================================