C言語の関数ポインタとは?

このQ&Aのポイント
  • C言語における関数ポインタとは、関数へのポインタを指す変数のことです。
  • 関数ポインタは、関数へのポインタを格納し、その関数を呼び出すことができます。
  • また、関数ポインタを使用することで、プログラムの柔軟性を高めることができます。
回答を見る
  • ベストアンサー

関数ポインタについて

C言語によるUNIXシステムにプログラミング入門という本を読みながらC言語を勉強しています。 しかし、サンプルとして提示された下記の内容の意味がわかりません。 分からない箇所が「関数ポインタ」と呼ばれるものがついているということが分かった程度で、どういう意図で記述されているのかがわかりません。 分からないプログラムの処理内容は、ファイル内のデータを16進数で表示するというものです。 分からない箇所を記します。 #include <stdio.h> #define BUFF 17 /*buffer*/ #define ERR -1 /*system call error*/ void usage(void); /*put usage message*/ char *command_name /*command name*/ FILE *fpin; /*file pointer*/ main(int argc,char *argv[ ]){ char *rindex(const char*s,int c); /*末尾から文字列検索*/ void hexdump(void); ... ... } void hexdump(void){ ... ... } void usage(){ ... ... } 不明なのは、main関数の中の char *rindex(const char*s,int c); /*末尾から文字列検索*/ void hexdump(void); です。 Cについて、不明なところが多いので、利用する関数は使う前に宣言しなければいけない程度の理解ですが、そうだとしてもusageメソッドはmain関数の外であるのに、rindexとhexdumpは何故main関数の中で宣言されているのでしょうか。 上記の不明点とは別で、rindexの前にポインタが付いていると思うのですが、hexdumpやusageにはついていません。 知人からは、関数までのポインタを返すとのことでしたが、用途もいまいち理解できません。 全てではなくてもいいので、ヒントをいただけるとうれしいです。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

これは関数ポインタではなくて、関数のプロトタイプ宣言ですね。プロトタイプ宣言では、関数がどのような引数をとり、どのような返り値を返すかというのを記述します。 char *rindex(const char*s,int c); /*末尾から文字列検索*/ void hexdump(void); はconst char*型の引数とint型の引数を取って、char*型を返すrindexという関数と何も引数をとらず何も返さないhexdumpという関数を定義しています。こうすることでmain関数の中でこれらの関数が使われた場合に正しく型を認識することができるようになっています。 今回、main関数の中でおそらくrindex関数とhexdump関数が使われているのでこのように宣言しているのではないでしょうか。 プロトタイプ宣言の有効範囲 (スコープ) をmain関数の中に限定するのは無意味なので、プロトタイプ宣言は#includeの直後などに書かれるのが普通だと思いますけどね。 ちなみに、rindexはstrings.hに定義されているので#include <stdio.h>のあとに#include <strings.h>と書けばこんな変なことはしないでいいです。また、void hexdump(void);もusage関数の次の行に普通は書くものだと思います。 個人的にはこのプログラムをメンテナンスするくらいなら書き直します。プロトタイプ宣言をmain関数の中と外(usage関数)に分けるなど、なぞなプログラミングスタイルをしてくれています。また、hexdump関数が引数をとらずにFILE *fpin; /*file pointer*/をグローバルで宣言するというのも嫌ですね。グローバル変数を使っている関数はその関数の再利用性が著しく損なわれるので、理由がないとこの実装はしません。あと、stdlib.hをincludeしているわけですから#define ERR -1 /*system call error*/はいらないのではないかと予想します。

anthis
質問者

お礼

ご回答ありがとうございました。 プロトタイプ宣言は、mainの外で行うものと決めつけていた為、混乱していました。 グローバル変数ときくと、いろいろな箇所から自由に扱えるように思えるのですが、再利用性が失われるというのはどういったシーンでしょうか。

その他の回答 (4)

回答No.5

stdlib.hに定義されているエラーの EXIT_FAILURE を無視する「#define ERR -1」とか http://www.geocities.jp/ky_webid/c/065.html strings.hに定義されている文字列sの中に,文字列tが最後に現れる位置を返す char *rindex(const char*s,int c); を「末尾から文字列検索」するといった受け狙い(?)の関数プロトタイプ宣言とか http://www.obihiro.ac.jp/~suzukim/masuda/octave/html/octave_28.html プロトタイプ宣言は、以前のCがint型関数は型名を明記しない慣習からバグが入り易い欠陥があり、事前にCコンパイラに知らせて誤りを検出する機能を強化するために設定された経緯を無視した main()関数内での書法解説とか http://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97 たかが、ファイル内のデータを16進表示するのに、FILE *fpin; はおろか main(int argc,char *argv[ ]){ と引数まで取り込む複雑なプログラミング内容と想像できる初心者向け入門書である(?)とか UNIX哲学を無視したC言語プログラミング入門書には、非常に興味があります。 http://en.wikipedia.org/wiki/Unix_philosophy 書籍名と著者を教えていただけませんか。 ちなみに、UNIXにおけるファイルの16進表示プログラムは↓のように簡単です。 使い方は UNIX哲学に習い、フィルターとしての機能を有し、次のようにコマンドラインを打ち込みます。 1.antis.c ソースファイルを 16進表示する。   ./a.out<antic.c 2.意味はないけどパイプを使う。   cat<antis.c | ./a.out /* Gcc on MacOSX * Source file name: antis.c */ #include <stdio.h> int main(void) { int c,count,offset,buf[3]; count=offset=0; printf("00000 "); while((c=getchar())!=EOF){ if(offset==0) { buf[1]=c; offset=1; }else{ buf[0]=c; printf("%02x%02x ",buf[0],buf[1]); offset=0; } count++; if(count%16==0) { printf("\n"); printf("%05x ",count); } } if(offset!=0) printf("%02x%02x ",0,buf[1]); printf("\n"); return 0; }

anthis
質問者

お礼

ご回答ありがとうございました。 ご提示いただいたサンプルソースコードもわかりやすくて助かります。 今参考にしている書籍は河野清尊氏の「C言語によるUNIXシステムプログラミング入門」という書籍です。

回答No.4

No.1のものです。 あのままでは、分かりづらいと思ったので、補足。 関数のポインタは、次のように宣言します。 char (*function_name)(parameter_list); 最初のカッコにより、function_nameは、ポインタであり、次のカッコが付くことにより、それが関数であり、型名によって、その評価結果(この場合関数の戻り値)がcharである。 となります。 parameter_listは、普通の関数宣言時と同じ。 使い方はこんな感じ。 #include <stdio.h> void hello(void){ printf("hello\n"); } int main(){ void (*function_name)(void); function_name=hello; (*function_name)(); printf("こっちはメイン\n"); }

anthis
質問者

お礼

ご回答ありがとう御座います。 1度目の投稿に加え、ソースコードまでご提示いただきありがとうございました。 関数ポインタの挙動について理解する事ができました。 ありがとうございました。

  • koi1234
  • ベストアンサー率53% (1866/3459)
回答No.2

全体がわからないので嘘言ってたらすいません >usageメソッドはmain関数の外であるのに、rindexとhexdumpは何故main関数の中で宣言されているのでしょうか。 メソッドというか関数ですね 基本的にはどちらで宣言しても問題はありませんが あえて分けられているのであれば そういつソース内の他の関数があってそちらでも(複数個所で) usage を使ってる rindexとhexdumpは main関数内でしか使わない ということなのかもしれません >関数までのポインタを返す 違います この言い方が適切か分かりませんが rindex 関数が charのデータ格納領域のアドレス(char *)を返す ということを意味します 関数までのポインタを返すわけではありません 例えば 文字列  abcde  がアドレス 100番地から順番にセットされていたとして rindex を 使う事により aの位置を検索する様な場合  アドレス 100 が返り bの位置を検索する様な場合  アドレス 101 が返り 以後 同様  といったような関数の仕様になっているということです (実際の処理内容に合わせていません あくまでイメージ) voidに関しては有効な値がない事を意味します

anthis
質問者

お礼

ご回答頂きありがとう御座います。 rindexの挙動について理解する事ができました。 おっしゃる通り、main関数内でのみ利用している関数だったため、main内で宣言していたようです。 ありがとうございました。

回答No.1

関数の宣言は、 記憶クラス 型名 関数名(パラメータ宣言) ですので、 char *が、charへのポインタ型と解釈されます。つまり、この関数はcharへのポインタを返す関数だということです。多分、末尾から検索して、見つけた文字へのポインタでも返す仕様なのでしょう。 また、関数のプロトタイプも、変数と同じように、宣言した場所によって、スコープルール(その名前の有効範囲)が制限されます。 つまり、rindexという関数は、main内でしか使えないはずです。 しかし、標準のCでは、関数は入れ子に定義できないので、あまり意味はないと思います。

関連するQ&A

  • ポインタのポインタの関数受け渡しについて

    現在ポインタのポインタを利用したプログラムを作成しています。 main関数で int **dt; と宣言したとして、配列のセットにはset関数を、 表示に関する処理をpt関数で行いたいと思っています。 void set(int ??); void pt(int ???); int main(void){ int **dt; set(??); pt(???); } void set(int ??){ dt = (int**)malloc(sizeof(int*) * k); for(i = 0; i < k; i++){ dt[i] = (int*)malloc(sizeof(int*) * k); } のように配列サイズの動的確保が目的 } void pt(int ???){ 二重forループ{ printf(dt[i][j]); } } ??、???には何を入れるべきかが理解できません。 ご教示のほどよろしくお願いいたします。

  • 関数ポインタの型をtypedefしたとき

    C言語において、関数ポインタの型をtypedefで作ると、 typedef int (*MyFunc)(int*,int*); と宣言でき、関数ポインタの変数は、 int FuncA(int* a, int* b) { ~ } void main_loop() { MyFunc pf = FuncA; ~ (*pf)(pa,pb); } というように使うと思います。 ここで疑問なのですが、この実際に呼び出される関数、FuncAの定義に、typedef(ここではMyFunc)を使えないものでしょうか? 同じことを2回やっているようで、無駄に思えてしまいます。

  • ポインタのポインタが引数にある関数の使い方。

    ポインタのポインタが引数にある関数の使い方。 現在、このポインタのポインタが引数にある関数の動きがわからず困っています。 int test(int ** head) { int * pTail = (int *)*head; pTail = pTail + 1; } もし、この関数を呼び出して使用した場合どのような動きをするのでしょうか? int * comm_msg; これをグローバルポインタ変数として宣言させて、 test((void **)&comm_msg); このように呼び出したとした場合とさせていただきます。

  • 関数へのポインタ

    超初心者です。 C言語を使ってsin波を生成して音を鳴らそうとしているのですが・・ネットで調べてもよくワカリマセン・・ 超初心者な私でも理解できるようなサイトを教えて下さい. また、関数へのポインタも勉強しているのですが, char *(*func)(void); といったchar 型へのポインタを返す関数へのポインタというのがあったとして,関数へのポインタは理解できたのですがさらにchar 型へのポインタとなると一体これが何を指しているのかさっぱりで・・・・ ご協力お願いします.

  • 不透明なデータのポインタ宣言

    ライブラリ内で使用し、ライブラリの外には、 中身を公開したくない構造体があります。 ライブラリ関数には必ず、ポインタとしてやりとりされます。 これを外部向けに不透明なデータのポインタとして宣言したいのですが・・ typedef const void *PRIVATE_DATA ; このように宣言してみたのですが、 これでは、否応にもvoidポインタとなってしまい、 char *等と暗黙のキャストが行われてしまいます。 たとえば・・ int lib_func( PRIVATE_DATA priv ) ; こんなライブラリ関数に対して、 lib_func( "HELLO" ) ; こんなおかしな呼び出し方が書けてしまいます。 不透明で、他の型へ暗黙のキャストが行われないポインタの宣言の仕方って、 ありませんでしょうか。。? ちなみに、コンパイラは gcc version 3.4.4 です。

  • 【C++】関数ポインタの代入

    C++の関数ポインタについて質問です。 下記のように関数ポインタを宣言し、3通りの代入を行ってみました。 (3)のように関数名の頭に&を付けた場合と(2)のように&を付けなかった場合で 全く動きが同じになってしまうのですが、何故なのでしょうか? ------------------------------------------ #include "stdafx.h" #include <iostream> using namespace std; void Func1(){ cout<<"Func1が呼ばれました。"<<endl; return; } int main() { //(1) void (*fp1_1)(); fp1_1 = Func1; fp1_1(); //(2) void (*fp1_2)()=Func1; fp1_2(); //(3) void (*fp1_3)()=&Func1; fp1_3(); getchar(); return 0; }

  • ■ 移植性のないポインタ変換について ■

    以下のプログラムは、指定したファイル名の存在を確認し、存在しなければ変数「flg」に「0」を代入するものです。 bcc32コンパイル時に「移植性のないポインタ変換(関数 AB )」と出てしまいます。 コンパイラの引数「-w-rpt」を使った回避以外に解決方法はございますでしょうか? よろしくお願いいたします。 #include <stdio.h> int AB(const char *); void main(void) { char A[] = "ABC.txt"; int flg; flg = AB(A); printf("flg = %d\n", flg); } int AB(const char *A_pt) { return(fopen(A_pt, "r")); }

  • malloc領域から関数ポインタ型で呼び出し

    現在C言語で,関数ポインタ型による関数の呼び出しを勉強しています. print()という関数は受け取った文字列を表示するだけの関数ですが, これを通常の関数ポインタを用いて実行したのがmain内の(*mem)("FROM POINTER.");です. これに対して後半は,mallocで確保した領域にmemcpyでprint()をコピーし, (*mem)("FROM COPIED AREA.");で実行しています. 実行環境は以下の通りです. OS:Windows7 Personal 32bit CPU:Intel Core i5 M430 統合開発環境:Visual C++ 2010 Express Edition コンパイルには成功しています. 実行すると"FROM POINTER."は表示されるのですが, "FROM COPIED AREA."は表示されず,プログラムが動作を停止してしまいます. mem = (void (*)(char*))tmp;までは実行できているようですが, (*mem)("FROM COPIED AREA.");の実行の時点で停止しているようです. またコマンドプロンプトからBorland C++ 5.5.1でコンパイルしても, 同じように動作が停止してしまいます. ただし同じプログラムでも,Cygwinからgccでコンパイルすると, 意図した通りの挙動となっていることを確認しています. コンパイラの違いによるものなのか, UNIXとWindowsの違いによるものなのか判断できずにいます. (もしくはライブラリのmalloc,memcpy辺りの実装法の違いでしょうか?) どなたかご存知でしょうか. ----------------------------------------- #include<stdio.h> #include<stdlib.h> #include<string.h> #define SIZE 512 void print(char* str){ printf("%s\n", str); } int main(int argc, char** argv){ void (*mem)(char*); void *tmp; mem = &print; (*mem)("FROM POINTER."); tmp = malloc(SIZE); memcpy(tmp, print, SIZE); mem = (void (*)(char*))tmp; (*mem)("FROM COPIED AREA."); }

  • 関数の戻り値にその関数のポインタを返すには?

    関数ポインタの使い方について質問したいです. 関数で,自分自身のポインタを(void*)ではなく関数と同じ型のポインタで返すことは可能でしょうか? (自分自身の関数ポインタと同じ型??) fucn (){ return fucn; } int main(){ fucn()()()(); return 0; } このようにmain関数の中でfucn()()・・・ のように任意個の()を書いたりできないでしょうか?  詳しい方いたらよろしくお願いします.

  • C言語のポインターに関する警告

    line[100]で 「1」が格納されていたら「a」 「2」が格納されていたら「b」 「3」が格納されていたら「c」 とout[100]に代入する関数を作りたいのですが コンパイルすると関数の部分で warning: assignment makes integer from pointer without a cast という警告がでます。 ポインターは使っていないのですが、ポインターに関する警告が出ているようで困っています。 どこが悪いのかまったくわからなくて作業が完全に止まってしまいました。 解決法をおしえてください。お願いします。 /*宣言*/ int=i; /*main関数内のfor文で使用*/ char line[100], out[100]; void change(int); /*関数*/ void change(int i)   {    if(line[i]=='1'){     out[10]="a\0"    }if(line[i]=='2'){     out[10]="b\0";    }if(line[i]=='3'){     out[10]="c\0" } }

専門家に質問してみよう