
C言語の深いところまで理解しようとしてます。今まで使わないだろうと思っていた関数へのポインタ
なのですが、
2分探索のところで
bsearch関数というのが出てきました。
この関数は第5引数に比較関数を引数にするのですが
p = bsearch(&ky,
x,
nx,
sizeof(int),
(int (*)(const void *,const void *))int_cmp
);
という形でサンプルソースには載っています。
この
(int (*)(const void *,const void *))int_cmp の部分なのですが、まず戻り値をキャストするなら
int (*)ではなく(int *)ではないでしょうか。
それとint_cmpは比較関数なのですが、引数が左側に来る、というところが納得できません。
ちなみにソースファイルをcppのままだとコンパイルできませんでした.cに拡張子を直したらコンパイルできました。
説明が足りないところがあったら指摘してください。お願いします。
No.7ベストアンサー
- 回答日時:
このプログラムを理解する前に、関数ポインタについて一通り勉強した方がいいと思います。
int_cmp を「引数無しの呼び出し」とか思っているようでは、いくら「正しい説明」をしても理解できないでしょう。
まず、最初にポインタのキャストについて。
値型のキャストは、その値を変化させることがあります
int i =1 ; double d;
d = (double)i
→ 整数1を実数1.0に変換。
ポインタのキャストでは、ポインタ自体も、その指し示す実体も、変化しません。
解釈だけが変わります
char *pc = {1,2,3,4} ;
これがアドレス100になる、とします
アドレス100=1
アドレス101=2
アドレス102=3
アドレス103=4
とメモリに並んでいます。ここで
int *pn ;
pn= (int * ) pc ;
とすると
・pc はアドレス100になります。
・アドレス100=1、アドレス101=2...はそのままで。
です。変化はありません。
しかし、解釈が違います
*pc → アドレス100の値→ 1
*pn → アドレス100からsizeof(int)=4つ分の値 → 0x04030201
となります
※ sizeof(int) の値や、どちらが上になるか、は環境の依存します。
# この例では、VisualC、IntelCPUで考えています。
関数ポインタのキャストでも同様で、中身は変化しません。
どんな引数や戻り値を持つ関数か、の解釈を変えるだけです。
キャストの書式について。
intへのポインタの宣言は
int * ポインタ変数名;
です。intへのポインタへのキャストは
(int *)q
です。キャストは、変数宣言から変数名を除いた形になっています。
関数ポインタの宣言は
戻り値の型 (* ポインタ変数名)(引数の型...) ;
です。よって、
この関数ポインタへのキャストは
(戻り値の型 (*)(引数の型...) )
です。
なお(* ポインタ変数名)となっているのは、優先順位の関係です。
括弧が無いと (戻り値の型 *) ポインタ変数名 と解釈され、 ([戻り値の型]へのポインタ)を戻り値に持つ[ポインタ変数名]という関数(の宣言)となります。
int (* fp)(double); /* 引数にdouble 1つを持ち、戻り値がintの関数へのポインタ変数fp の宣言 */
int * fp(double); /* 引数にdouble 1つを持ち、戻り値が int*(intへのポインタ) の関数fpの宣言 */
*pn → アドレス100からsizeof(int)=4つ分の値 → 0x04030201
これがリトルエンディアンで格納されてるとかはわかるのですが
>関数ポインタの宣言は
>戻り値の型 (* ポインタ変数名)(引数の型...) ;
>です。よって、
>この関数ポインタへのキャストは
>(戻り値の型 (*)(引数の型...) )
>です。
(*)このお下劣な書式の意味がわかりました。
関数へのポインタをいろいろ調べたいと思います。
ベストアンサーにしたい回答ありがとうございました。
No.10
- 回答日時:
質問者のために補足しておくと, #9 にある
「(型の内容)引数名」というキャスト
というのは一般的な表現ではありません.
少なくとも, こんなところで「引数」という表現を使うのは一般的ではない.
No.9
- 回答日時:
>(int (*)(const void *,const void *))int_cmp
これは「(型の内容)引数名」というキャストです。
キャストの「括弧」の中が「型」です。
(int (*)(const void *,const void *))
から「キャスト」を意味する「外側の括弧」を取り払うと
int (*)(const void *,const void *)
になります。
この「型」は
戻り値の型 ホゲホゲ(引数の型,引数の型)
の書式になっていますから、「関数のホゲホゲ」です。一対の「括弧」が「関数」を意味します。
「関数のホゲホゲ」の「ホゲホゲ」は「(*)」と書いてあります。
「(*)」は「右に書いてあるモノはポインタですよ」を意味します。
これは、変数や引数の宣言時の「*param」の「*」と同じで「paramはポインタですよ」と同じ書き方です。
つまり「関数のポインタ(関数の実体が居るアドレス)」って事になります。
そして、その関数は「intを返す」と書いてあります。
最後に、全体が「括弧」で囲まれてて「これはキャストですよ」って書いてあります。
>それとint_cmpは比較関数なのですが、引数が左側に来る、というところが納得できません。
「(型の内容)引数名」というキャストですから、引数名は「一番右」です。
それに「引数」は何処にも書かれていませんよ。「引数の型」なら、内側の括弧の中に書いてありますが。
No.8
- 回答日時:
>関数のポインタというのを自分は理解できていません。
。でしたら、まず関数のポインタを理解してください。
>このサンプルソースは関数のポインタのキャスト??なのですか?
そのサンプルソースが関数のポインタのキャストの説明を意図したものかどうか私にはわかりません(質問内容からはその辺は全く読み取れませんし)。
ですが少なくとも
(int (*)(const void *,const void *))
は関数ポインタへのキャストです。
>int_cmpが変化するわけではない、、ということは??表現するならint_cmpの引数を一時的にコンバートする、というのがポインタでしょうか??この表現であってますか?
呼ばれてもいないint_cmpの引数の一時的コンバートをどこでするんですか?
関数ポインタへのキャスト
ちょっと敷居が高いようです。
すぐbsearch関数2回目という質問をしようと思っています。
またよろしくお願いします。
No.6
- 回答日時:
int a[] = {1, 2, 3, 4};
のとき、
a と、&a[0] が同一視されるというのと同じように、
int int_cmp(const void* p1, const void* p2);
で、int_cmp は、関数の名前であり、関数のアドレスを表す
ポインタとして扱われます。
この回答への補足
ということは第5引数はint_cmpを引数なしで呼び出してる、のではなく、
int_cmpの格納された先頭アドレスを参照していて、これを関数へのポインタ、と呼ぶのでしょうか?
No.5
- 回答日時:
#2 さんのソースを少し変えると、
#include <stdio.h>
#include <stdlib.h>
int int_cmp(const void* x, const void* y) {
return *(int*) x - *(int*) y;
}
int main(void) {
int x[] = {13, 11, 9, 7, 5, 3, 1};
int ky = 11;
int *p, i;
qsort(x, sizeof(x) / sizeof(int), sizeof(int), int_cmp);
for (i = 0; i < sizeof(x) / sizeof(int); i++)
printf("%d ", x[i]);
puts("");
p = bsearch(&ky, x, sizeof(x) / sizeof(int), sizeof(int), int_cmp);
if (p != NULL) {
printf("%d found at x[%ld]\n", *p, p - x);
}
return 0;
}
逆順で、x[] を初期化しておいて、qsort でソート、
bsearch でバイナリサーチしています。
関数の名前は、ソースコード上で使うと、関数ポインタとなるので、
このようにできます。
関数の名前を使うと、関数ポインタとみなされるということが、
理解の妨げになっているのかもしれません。
この回答への補足
>関数の名前は、ソースコード上で使うと、関数ポインタとなるので、
>このようにできます。
>関数の名前を使うと、関数ポインタとみなされるということが、
>理解の妨げになっているのかもしれません。
関数の名前をソースコード上で使う、コンストラクタみたいなものですか??
printf("%d found at x[%ld]\n", *p, p - x);
この辺のp-xがp[x]のシンタックスシュガーというあたりまでは何となく調べられましたが
p+xと手元にあり、、
降順と何か関連があるのか・・・
bsearch関数が解らない2 という質問を近日中にするつもりです。
その時またよろしくお願いします。
No.4
- 回答日時:
>>(int (*)(const void *,const void *))int_cmp
>
>の一行なのですが、これって
>int_cmpを引数なしで呼び出してますよね?
呼び出していません。
int_cmp関数のアドレスを参照してるだけです。
>しかしint (*)(const void *,const void *))int_cmp
>
>のまず int (*) がわかりません。 *は(よりも優先順位が低いため、というのは調べたのですが
>(int *)と書いたらエラーでしょうか。
仮にint (*)を(int *)と書き換えると
((int *)(const void *, const void *))
になりますけど、どういう意味になるのか教えてもらえますか?
>関数をキャストというのは初耳で難しいのですが
>int (*)(const void *,const void *)
>と書くと2つのvoidがたを引数にし、int型ポインタを返す関数にint_cmpが変化するわけですね??
int_cmpが変化するわけではないです。
>それなら、int_cmpの宣言のときにint int_cmp(void *x,void *y)
>と書いたらよい話ではないかと考えてしまいます。
書き換えるなら
int int_cmp(const void *, const void *)
関数のポイントのキャストを理解する前に、関数のポインタを先に理解すべきじゃないかなぁ
この回答への補足
興味深々な回答です。
>関数のポイントのキャストを理解する前に、関数のポインタを先に理解すべきじゃないかなぁ
関数のポインタというのを自分は理解できていません。。
このサンプルソースは関数のポインタのキャスト??なのですか?
>>関数をキャストというのは初耳で難しいのですが
>>int (*)(const void *,const void *)
>>と書くと2つのvoidがたを引数にし、int型ポインタを返す関数にint_cmpが変化するわけです>ね??
>int_cmpが変化するわけではないです
int_cmpが変化するわけではない、、ということは??表現するならint_cmpの引数を一時的に
コンバートする、というのがポインタでしょうか??この表現であってますか?
みなさまからご指導いただきいささか興奮しすぎて
bsearch関数が解らない2という質問を近々まとめるために
しようと思っています。
その時またよろしくお願いします
No.3
- 回答日時:
> しかしint (*)(const void *,const void *))int_cmp
> のまず int (*) がわかりません。
intを返す関数(へのポインタ)です。
> *は(よりも優先順位が低いため、というのは調べたのですが
> (int *)と書いたらエラーでしょうか。
やってみれば?
> それなら、int_cmpの宣言のときにint int_cmp(void *x,void *y)
> と書いたらよい話ではないかと考えてしまいます。
それでいいんじゃないですか?
ここでは「与えられた関数を書き換えることができないので(bsearchが受け取れる型に)キャストする」
サンプルと解釈できます。
No.2
- 回答日時:
#include <stdio.h>
#include <stdlib.h>
int int_cmp(const int* x, const int* y) {
return *x - *y;
}
int main() {
int x[] = { 1, 3, 5, 7, 9, 11 ,13 };
int nx = 7;
int ky = 11;
int* p;
p = bsearch(&ky,x,nx,sizeof(int),
(int (*)(const void *,const void *))int_cmp
);
if ( p != NULL ) {
printf("%d found at x[%d]\n", *p, p-x);
}
return 0;
}
> (int (*)(const void *,const void *))int_cmp の部分なのですが、まず戻り値をキャストするなら
> int (*)ではなく(int *)ではないでしょうか。
> それとint_cmpは比較関数なのですが、引数が左側に来る、というところが納得できません。
int_cmp は"引数として const int* を2つ取り、intを返す関数"です。
ここでは int_cmp をキャストしています。 int_cmpの戻り値をキャストしているのでは「ありません」。
bsearchの第5引数に与えるのは"引数として const void* を2つ取り、intを返す関数"でなくてはなりません。
int_cmpはそのままでは型が一致しないため、"引数として const void* を2つ取り、intを返す関数"にキャスト
しています。引数が左側に来ているのでは「ありません」 int (*)(const void *,const void *) にキャストしているのです。
> ソースファイルをcppのままだとコンパイルできませんでした
C++はCに比べて型に厳しいため、(bsearchの返す)void*からint*への暗黙の型変換を許しません。
正しくは: p = (int*)bsearch(&ky,x,nx,sizeof(int),...
この回答への補足
すいませんありがとうございます。
まずcppでコンパイルできなかった理由はわかりました。
しかしint (*)(const void *,const void *))int_cmp
のまず int (*) がわかりません。 *は(よりも優先順位が低いため、というのは調べたのですが
(int *)と書いたらエラーでしょうか。
>
>bsearchの第5引数に与えるのは"引数として const void* を2つ取り、intを返す関数"でなくてはな>りません。
>int_cmpはそのままでは型が一致しないため、"引数として const void* を2つ取り、intを返す関>>数"にキャスト
>しています。引数が左側に来ているのでは「ありません」 int (*)(const void *,const void *) に>>キャストしているのです。
関数をキャストというのは初耳で難しいのですが
int (*)(const void *,const void *)
と書くと2つのvoidがたを引数にし、int型ポインタを返す関数にint_cmpが変化するわけですね??
それなら、int_cmpの宣言のときにint int_cmp(void *x,void *y)
と書いたらよい話ではないかと考えてしまいます。
No.1
- 回答日時:
詳しい人が回答してくれると思いますが…
>(int (*)(const void *,const void *))int_cmp の部分なのですが、まず戻り値をキャストするなら
>int (*)ではなく(int *)ではないでしょうか。
キャストの範囲が違いますね。
「int型の戻り値を返し、引数としてconst void *を2つ必要とする関数へのポインタ」のキャストです。
>それとint_cmpは比較関数なのですが、引数が左側に来る、というところが納得できません。
引数が左側に来ているのではなく、「ポインタの型」として「const void *を2つ必要とする関数」を指定している。ということかと。
別の引数を利用する関数を渡されても困りますしね。
この回答への補足
>(int (*)(const void *,const void *))int_cmp
の一行なのですが、これって
int_cmpを引数なしで呼び出してますよね?
ポリモーヒズムみたいな機能がCにあるのですか??
みなさまからご指導いただきいったん自分の中で整理したあと
再度bsearch関数が解らない2という質問をしようと思っています。
その時またよろしくお願いします。ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- C言語・C++・C# const char** p;のとき、free(p)でC4090エラーとなるのはなぜですか 3 2023/03/31 16:28
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# C言語 ポインタ 配列 2 2022/06/02 17:29
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# 至急教えてください! プログラミングの問題です! お願いします! 出力2と全く同じ出力をするように、 2 2022/06/22 23:10
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# 関数ポインタの高速化のメリット 7 2023/05/05 20:15
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Run-Time Check Failure #3とい...
-
init関数の意味
-
C言語のポインタに直接アドレス...
-
コンストラクタでnewを失敗した...
-
可変長のリスト
-
高校1年です。情報技術基礎のシ...
-
CWnd::EnableWindow()の扱い方
-
単方向リストの解釈
-
C言語の文字列?処理 strcpyやl...
-
構造体とfscanf
-
NULLとブランクの違い
-
自作DLLの引数について、ポイン...
-
アプリを32bitから64bit移行
-
セグメントエラー
-
size_t
-
localtime() 関数についての質問
-
長さゼロの文字列の適切な名称
-
戻り値で構造体を返すことは可...
-
VBはCを混乱させる?
-
連結リスト 要素の入れ替え
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語のポインタに直接アドレス...
-
fopne で失敗する原因
-
戻り値で構造体を返すことは可...
-
LPSTR型の初期化について
-
Run-Time Check Failure #3とい...
-
ExcelVBAでのkernel32(64bit)
-
参照型で受け取った引数をポイ...
-
init関数の意味
-
セグメントエラー
-
アプリを32bitから64bit移行
-
ハンドルはポインタか
-
ハンドル、アドレス、ポインタ...
-
C言語でのconstを返す関数
-
C++で関数ポインタから関数名を...
-
パスからファイル名を抽出
-
ReadFileの読み込みエラーについて
-
#define NULL ((void *)0) の弊害
-
CImage GetBitsメソッドについて
-
ポインタ変数の疑問
-
Cで作成したDLL関数をVBから呼...
おすすめ情報