重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

電子書籍の厳選無料作品が豊富!

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にはついていません。
知人からは、関数までのポインタを返すとのことでしたが、用途もいまいち理解できません。
全てではなくてもいいので、ヒントをいただけるとうれしいです。
よろしくお願いします。

A 回答 (5件)

これは関数ポインタではなくて、関数のプロトタイプ宣言ですね。

プロトタイプ宣言では、関数がどのような引数をとり、どのような返り値を返すかというのを記述します。
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*/はいらないのではないかと予想します。
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/11/28 16:44

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/ …

プロトタイプ宣言は、以前のCがint型関数は型名を明記しない慣習からバグが入り易い欠陥があり、事前にCコンパイラに知らせて誤りを検出する機能を強化するために設定された経緯を無視した main()関数内での書法解説とか
http://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0% …

たかが、ファイル内のデータを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;
}
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/11/28 16:40

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");
}
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/11/28 16:48

全体がわからないので嘘言ってたらすいません


>usageメソッドはmain関数の外であるのに、rindexとhexdumpは何故main関数の中で宣言されているのでしょうか。

メソッドというか関数ですね
基本的にはどちらで宣言しても問題はありませんが
あえて分けられているのであれば
そういつソース内の他の関数があってそちらでも(複数個所で) usage を使ってる
rindexとhexdumpは main関数内でしか使わない ということなのかもしれません

>関数までのポインタを返す
違います この言い方が適切か分かりませんが
rindex 関数が charのデータ格納領域のアドレス(char *)を返す
ということを意味します
関数までのポインタを返すわけではありません

例えば 文字列  abcde  がアドレス 100番地から順番にセットされていたとして
rindex を 使う事により
aの位置を検索する様な場合  アドレス 100 が返り
bの位置を検索する様な場合  アドレス 101 が返り
以後 同様  といったような関数の仕様になっているということです
(実際の処理内容に合わせていません あくまでイメージ)

voidに関しては有効な値がない事を意味します
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/11/28 16:50

関数の宣言は、


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

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!