C言語:関数のポインタ/コールバック関数



既存のポインタとは、データセグメントまたはスタックセグメントの変数のアドレスまたはメモリアドレスを格納するために使用されるデータポインタです.
このセクションでは、コード・セグメント内の文のアドレスを格納し、ポインタとして使用する方法について説明します.
関数ポインタ
  • 特定関数を構成する開始コマンドの位置へのポインタ.
  • int sum(int a, int b)
    {
        int result = 0;
        result = a + b;
        return result;
    }
    
    
    int result = sum(2,3);
  • は一般的にこのように呼ばれている.
  • 関数ポインタを使用するには、関数プロトタイプを使用してポインタを宣言します.
  • int sum(int a, int b); == int(*p)(int, int); //*p로 sum함수를 호출한다.
                               p = ∑ //sum의 함수의 주소를 p에 저장한다.
    なぜ関数のポインタを使用しますか?
    ->同じ形式の関数をグループ化できるからです.
    同じフォーマットは(パラメータ、資料型、同じ形式の戻り値)です.
    次の例です.
     <덧셈>
     int sum(int a, int b)
     {
        return a+b;
     
     }
     
     <뺄셈>
     int sub(int a, int b)
     {
       return a-b;
     
     }
     <곱셈>
     int Mul(int a, int b)
     {
       return a * b;
     
     }
     
     <나눗셈>
     int Div(int a, int b)
     {
       return a/b;
     
     }
     
     ==============================
     int result 1, result2, result3,result4;
     result1 = sum(8,2);
     result2 = sub(8,2);
     result3 = Mul(8,2);
     result4 = Div(8,2);
     
     이 4개의 함수를 포인터 하나로 묶을 수 있다.
     
     int (*p[4])(int , int) = {&sum, &sub, &Mul, &Div};
     
     여기서 *p([4]) 와 (*p)[4]의 차이를 모른다면 포인터 부분을 다시 공부하도록 하자.
     
     ==========================================================
     int (*p[4])(int int) = {&sum, &sub, &Mul, &Div};
     int result[4], i; // 함수를 호출 했을 때 반환되는 값 4개를 저장할 배열을 선언
     for(i = 0; i < 4; i++) result[i] = (*p[i])(8,2); 

    <関数ポインタを使用して繰り返し文で呼び出す>

    #include <stdio.h>
    
    int sum(int a, int b)
    {
        return a + b;
    
    }
    
    int sub(int a, int b)
    {
        return a - b;
    
    }
    int Mul(int a, int b)
    {
        return a * b;
    
    }
    int Div(int a, int b)
    {
        return a / b;
    
    }
    void main()
    {
        int (*p[4])(int ,int) = { &sum, &sub, &Mul, &Div };
        char table[4] = { '+', '-', '*', '/' };
        for (int i = 0; i < 4; i++)
        {
            printf("%d %c %d = %d\n", 8, table[i], 2, (*p[i])(8, 2));
        }
    }

    <結果>



    コールバック関数
  • プログラマーは、自分のコードを露出できないため、コードをコンパイルし、ライブラリ(library,*lib)形式のファイルとして提供します.
  • ライブラリの関数がどのような形式で宣言されているかを知っているだけで、コードを詳細に表示できないユーザーはこれらの関数を使用することができますので、関数のプロトタイプをヘッダ(header.*h)ファイルに書いて一緒に使用することができます.
  • たとえば、2つの整数の合計関数をライブラリ形式で提供し、それらを統合するとします.
  • ライブラリユーザー->sumの場合.libとsum.hはすべて提供しなければならない.
  • -라이브러리 프로그래머가 만든 헤더 파일과 라이브러리 파일
    <sum함수의 원형>
    sum.h
    1. int sum(int a, int b);
    < 값을 합산하는 함수>
    sum.lib
    int sum(int a, int b)
    {
      return a + b;
    }
    
    
    -라이브러리 사용자가 사용하는 형태
    
    #include "sum.h"
    #pragma commnet(lib, "sum.lib")
    
    void main()
    {
       int result = sum(2,3);
    
    }
    ※pragmaプリプロセッサを使用すると、コンパイラの各種設定値を変更できます.
    上で使用したフォーマットはsumです.これは、libファイルがプログラムで使用されることを意味します.
    プログラマが2つの数値を負に変換するように要求した場合、プラスに変換する関数を追加します.sumbsという新しい関数を追加する必要があります.
    -라이브러리 프로그래머가 만든 헤더 파일과 라이브러리 파일
    <sum함수의 원형>
    sum.h
    1. int sum(int a, int b)
    2. int sumabs(int a, int b)
    
    sum.lib
    
    int sum(int a, int b)
    {
      return a + b;
    
    }
    
    int sumabs(int a, int b)
    {
      if(a<0) a = a * (-1);
      if(b<0) b = b * (-1);
         
         
         return a + b;
    }
    
    -라이브러리 사용자가 사용하는 형태
    
    #include "sum.h"
    pragma commnet(lib, "sum.lib")
    
    
    void main()
    {
      int result1, result2;
      result1 = sum(2,-3);
      result2 = sumabs(2, -3);
    
    }
    

  • ライブラリに含まれる関数は、既存の機能を維持し、ユーザーが必要とするときに関数を変更する機能を提供することが望ましい.

  • ただし、プログラマはライブラリソースコード全体を提供できません.では、ライブラリユーザーはどのようにしてソースコードなしでライブラリを変更し、使用しますか?

  • 関数をパラメータとして関数ポインタを使用すればよい.
  • void myabsolute(int *p)
    {
      
        if(*p<0) *p = (*p) * (-1);
    
    }
    
    int sumabs(int a, int b)
    {
        myabsolute(&a); // if (a<0) a = a * (-1)
        myabsolute(&b); // if (b<0) b = b * (-1)
        
        return a + b;
    
    }
  • ユーザーがmyabssoluteのような関数を作成し、既存のライブラリのsumabs関数を呼び出すときに一緒に使用したい場合、どうすればいいですか?
  • 既存のライブラリにはmyabsouteという関数はありません.今回学習した関数ポインタを用いて,関数名を必要とせずに関数を呼び出す.
  • myabsolute関数のプロトタイプはvoid myabsolute(int*)である.
  • void (*p)(int *); 함수의 주소를 받아서 사용할 수 있다.
    
    ================================================
    
    int sumabs(int a, int b, void (*fp_abs)(int*))
    {
          (*fp_abs)(&a);
          (*fp_abs)(&b);
          
          return a + b;
    
    }
    
    =================================================
    
    sumabs(5,-1, &absolute); //sumabs함수에서 myabsolute 함수를 호출한 것과 같음
    
    sum(5, -1, NULL); //sumabs 함수에서 myabsolute 함수를 사용하지 않는 경우
    
    ==================================================
    
    <NULL에 대한 예외처리>
    
    
    int sumabs(int a, int b, void (*fp_abs)(int *))
    {
         if(fp_abs != NULL) (*fp_abs)(&a);
         if(fp_abs != NULL) (*fp_abs)(&b);
         
         return a + b;
    }
    関数ポインタを使用して、異なるa、b変数に設定して使用します.
    -라이브러리 프로그래머 시점
    
    <함수 원형>
    -sum.h-
    1. int sum(int a, int b. void(*pa)(int *), void(*pb)(int *))
    
    -sum.lib-
    
    int sum(int a, int b, void (*pa)(int *), void (*pb)(int *))
    {
        if(NULL != pa) (*pa)(&a);
        if(NULL != pb) (*pb)(&b);
        
        return a + b;
    
    
    }
    
    =============================================================
    
    #include "sum.h"
    #pragma comment(lib, "sum.lib")
    
    void main()
    {
      int result = sum(-3, -2, NULL, NULL);
    
    }
  • を使用すると、sum関数の3番目、4番目のパラメータの関数ポインタがNULL値に割り当てられるため、sum関数のif文は同時に処理されません.5返却
  • #include "sum.h"
    pragma commnet(lib, "sum.lib")
    
    
    void myabsolute(int* p)
    {
    	if (*p < 0) *p = (*p) * (-1);
    
    }
    
    
    void main()
    {
    
    	int result = sum(-3, -2, myabsolute, NULL);
    
    }
    ==============================================================
    -라이브러리 프로그래머 시점
    
    <함수 원형>
    -sum.h-
    1. int sum(int a, int b. void(*pa)(int *), void(*pb)(int *))
    ===============================================================
    -sum.lib-
    int sum(int a, int b, void (*pa)(int *), void (*pb)(int *))
    {
        if(NULL != pa) (*pa)(&a);
        if(NULL != pb) (*pb)(&b);
        return a + b;
    }
    ==============================================================
    ※호출관계를 파악해보자.
    
    sum(-3,-2,myabsolute, NULL) <==> 
    int sum(int a, int b, void(*pa)(int *), void (*pb)(int *))
    
    
    1. sum 함수의 pa변수가 NULL이 아니기 때문에 if(NULL != pa)(*pa)(&a); 문장이 수행된다.
    2. sum의 pa 변수 == myabsolute // (*pa)(&a) == myabsolute(&a) 
    3. sum 함수의 매개변수 a의 주소 값이 myabsolute 함수의 포인터 변수 p에 저장된다.
    4. myabsolute 포인터 변수인 p는 변수 a의 주소 값을 저장하고 있다. 
    5. 따라서 p가 가리키는 값은 음수인 -3이라서 if(*p<0) *p = (*p) * (-1); 조건문이 실행된다.
    6. 함수의 포인터 pb는 NULL 값이 저장되어 있기 때문에 if(NULL!=pb) (*pb)(&b); 조건문은 실행 x
    7. sum함수의 변수 a값이 -3에서 3으로 변경되었기 때문에 3 + (-2) 가 수행 -> 1이 반환