◎1---------------------------
#include<stdio.h>
void (*putdata)(int d); //// (1) ////
void english(int dt);
void japanese(int dt);
int main( )
{
int a=234;
putdata=english;
putdata(a);
putdata=japanese;
putdata(a);
return 0;
}
void english(int dt)
{
printf("Value is %d.\n",dt);
}
void japanese(int dt)
{
printf("数値は%dです.\n",dt);
}
---------------------------------
◎2--------------------------------
#include<stdio.h>
int (*get_sign) ( ); //// (2) ////
int iget_sign(int *a);
int dget_sign(double *a);
int main( )
{
int sgn,idt=6;
double fdt=-2.123;
get_sign=iget_sign;
sgn=get_sign(&idt);
printf("data:%d sgn:%d\n",idt,sgn);
get_sign=dget_sign;
sgn=get_sign(&fdt);
printf("data:%f sgn:%d\n",fdt,sgn);
return 0;
}
int iget_sign(int *a)
{
if(*a==0) return 0;
else if(*a>0) return 1;
else return -1;
}
int dget_sign(double *a)
{
if(*a==0.0) return 0;
else if(*a>0.0) return 1;
else return -1;
}
---------------------------------
以上2つのプログラムについて疑問があります。
◎1の「putdata=english;」と関数のアドレスをコピーするにおいて、(1)の部分の引数の「(int d)」がどういう意味があるか分かりません。
次に◎2の(2)の部分で、「int (*get_sign) ( );」と引数を空にするというのが理解できません。本当は(2)の部分を、「int (*get_sign)(void *a);」
にしなくてはいけないと参考書に書いてあったのですが、何故そうしなくてはいけないかも理解できません。
C++で実行すると、チェックが厳しいということで、エラーが出てしまうというのは、参考書には書いてあったのですが、その前に(1)、(2)での関数プロトタイプの引数の表現方法が理解できません。
教えていただけると嬉しいです。
No.7ベストアンサー
- 回答日時:
int foo ();
という関数の宣言があった場合,
C : fooはint型の値を返す関数
C++ : fooは「引数をとらず」int型の値を返す関数
と,関数宣言の引数として何も書かなかった時の意味合いが異なってきます。
また,Cでは任意の関数へのポインタ型の値を任意の関数へのポインタ型に変換できますが,
C++ではこの変換は存在しません。
# キャストと変換は異なります。
このため,C++ではそもそもコンパイル時にエラーとなります。
ご回答ありがとうございます。
>Cでは任意の関数へのポインタ型の値を任意の関数へのポインタ型に変換で
>きますが,
>C++ではこの変換は存在しません。
># キャストと変換は異なります。
上記の内容とても納得できました!
確かにC++でvoid型ポインタによるキャスト変換は出来ました。
「int foo( );」と引数を空にすると、C++では引数を勝手にvoidとしてしまうから実行出来ないという認識は合っているでしょうか?
No.6
- 回答日時:
> 本当は(2)の部分を、「int (*get_sign)(void *a);」
> にしなくてはいけないと参考書に書いてあったのですが、何故そうしなくてはいけないかも理解できません。
ここは参考書が間違っています。
なので,理解する必要もないです。
以下,国際標準規格ISO/IEC 9899:1999 Programming languages - C (ISと略します) を元に「間違っていること」の説明をします。
int *やdouble *とvoid *はcompatibleな型ではないため,int (*)(void *)とint (*)(int *)はcompatibleな型ではありません。
# IS 6.2.7 Compatible type and composite type - Paragraph 3 / IS 6.7.5.1 Pointer declarators - Paragraph 2
このため,get_signにiget_signを代入してget_signに関数呼び出し演算子を適用した結果は,未定義動作となります。
# IS 6.5.2.2 Function calls - Paragraph 9 / IS 6.3 Conversions - 6.3.2.3 Pointers - Paragraph 8
それに対して,片方のみにparameter type listがある状態になる,int (*)()とint (*)(int *)はcompatibleな型になります。
# IS 6.2.7 Compatible type and composite type - Paragraph 1, 3
このため,「int (*get_sign)(void *a)」と「してはならない」のが正しいです。
# 未定義動作とは,動作の結果がどうなるかわからないものです。「鼻から悪魔」などと形容されたりします。
ちなみに,ポインタの値の表現は,元となる型によって異なってもかまいません。
# IS 6.2.6 Representations of types - 6.2.6.1 General - Paragraph 1
それなのになぜvoid *が許されるかというと,void *への変換またはvoid *からの変換であることをコンパイラが知っているからです。
void *へのorからの変換については,その規定があるために,コンパイラはそれが正しく動作するように実装することが求められます。
ただし,void *へのorからの変換についても,関数へのポインタについては求められないことになっています。
# IS 6.3 Conversions - 6.3.2.3 Pointers - Paragraph 1
ご回答ありがとうございます。
C初心者なもので、詳しくは理解できませんでした。すいません。
◎2について、 (2)の部分を「int (*get_sign) ( );」や「int (*get_sign) (void *a);」としCとして実行したら実行できましたが、C++では出来ませんでした。
これは何故なのでしょうか?
No.5
- 回答日時:
No4の回答のお礼への回答
・void*について
> void型という変数*aと認識したのですが、
まちがっています。
void型の変数*aではなく、void*型の変数aです。
void*型は、「型は未定な変数の先頭アドレス」を指します。
int*型は、「型がintな変数の先頭アドレス」を
double*型は、「型がdoubleな変数の先頭アドレス」をそれぞれ指します。
void* aを(int*)aとすると「型がintな変数の先頭アドレス」、
void* aを(double*)aとすると「型がdoubleな変数の先頭アドレス」
と解釈することができて、それぞれの変数にアクセスできるというわけです。
・void (*putdata)( );について
int sub()とint sub(void)は異なります。
引数に明示的にvoid型を宣言していない関数は、「可変引数の関数」ということになります。
void型を宣言すると、「引数を持たない関数」です。
ご回答ありがとうございます。
>void型の変数*aではなく、void*型の変数aです。
>void*型は、「型は未定な変数の先頭アドレス」を指します。
>int*型は、「型がintな変数の先頭アドレス」を
>double*型は、「型がdoubleな変数の先頭アドレス」をそれぞれ指します。
上記のご回答の先頭アドレスを渡すという考え理解できました。
ありがとうございます。
No.4
- 回答日時:
「void 型」の変数を宣言することはできません. 当然, 関数の仮引数でもアウト.
あと,
void (*putdata)( );
と宣言すると, putdata は「値を返さない関数へのポインタ」となります. 一方
void (*putdata)(int *);
では「int * を引数にとり値を返さない関数へのポインタ」です.
関数へのポインタや関数のプロトタイプ宣言において () の中を空にすると「引数に対する情報 (引数の数や個々の引数の型) を一切与えない」ということを意味します. 関数を定義するときには, これとは異なり「空 = 引数を持たない」と解釈されます.
ご回答ありがとうございます。
>「void 型」の変数を宣言することはできません
とご回答してくれましたが、
No.3の回答者様が『何かのポインタ型』という宣言の仕方が『void*』になります。と回答してくれたのですが、「int (*get_sign) (void *a);」はint型にもdouble型にも対応できる、void型という変数*aと認識したのですが、この認識は間違っているのでしょうか?
◎1、2の(1)、(2)「void (*putdata)( );」「int (*get_sign) ( );」のように引数を書かないと2つのプログラムとも実行できたのが、いまいち理解できません。
教えていただけると嬉しいです。
No.3
- 回答日時:
1)については引数の型をあわせるためです
putdataの実際に呼び出されるEnglish/Japaneseは int型の引数を取ります
void(*putdata)();
と宣言してしまうと
putdata(a);
などといった使い方をする場合プロトタイプ宣言した物とは違う物と認識されコンパイルエラーになる可能性が高いでしょう
2)については 引数の型が int*とdouble*の2種類あります
この場合
int (*get_sign)(int *);
とした場合
get_sign = dget_sign;を実行しようとすると引数の型が違うのでエラーになると思います
そこで何かのポインタ型の引数を取る関数ポインタといった宣言方法にしないと面白くありません
この『何かのポインタ型』という宣言の仕方が『void*』になります
場合によって実際の型が変わります
get_sign = iget_sign;
とすれば引数は int*
get_sign = dget_sign;
とすれば引数は double*
といった具合に解釈されます
この回答への補足
自分でもいろいろと勉強してみました。
例えば、「void *p;」などのvoid型ポインタは宣言できると分かりました。
それで「*(int*)p;」や「*(double*)p;」と他の型にみなす事が出来るという事ですね。
1. ◎2について、「int (*get_sign) ( );」と引数を空にすると引数の型と個数をチェックしないという事で、実行できたのだと自分は認識しました。次に、「int (*get_sign) (void *a);」とすると、void型ポインタという事で実行できたと認識しました。
以上は、Cだから出来たのだと思います。
2. C++の場合「int (*get_sign) ( );」と引数を空にすると( )内はvoidと改められてしまうため、実行できないのかなと思いました。
それならばと、「int (*get_sign) (void *a);」としても「'int (__cdecl *)(int *)' から 'int (__cdecl *)(void *)' に変換できません。」とエラーが出てしまいました。これも( )内がvoidとみなされてしまったのかなと思いました。
1.の考えは合っていますでしょうか?
次の2.の考えは合っていますでしょうか?それならばC++で実行可能にするにはどうすればよいのでしょうか?
教えていただければ嬉しいです。
ご回答ありがとうございます。
>『何かのポインタ型』という宣言の仕方が『void*』になります
>場合によって実際の型が変わります
>get_sign = iget_sign;
>とすれば引数は int*
>get_sign = dget_sign;
>とすれば引数は double*
以上のご回答納得できました!
◎1について、◎2のように(1)の部分を「void (*putdata)(void d);」として何かの変数型dとするとエラーが出てしまいました。この表現はだめなのでしょうか?
しかし、「void (*putdata)( );」とすると何故か実行できました。
何故なのか教えてもらえると嬉しいです。
後、「void (*putdata)( );」のようの引数を空にすると、引数はvoidと認識されるのでしょうか?
No.1
- 回答日時:
>◎1の「putdata=english;」と関数のアドレスをコピーするにおいて、(1)の部分の引数の「(int d)」がどういう意味があるか分かりません。
(1)の部分は、「putdataは、int型の引数を取り、戻り値がない関数へのポインターである」と読みます。
つまり、english関数やjapanese関数のような、
「int型の引数を取り、戻り値がない関数」を代表しています。
よって、
>putdata=english;
>putdata(a);
この2行で
english(a);
と同じ意味を持ちます。
>(1)の部分は、「putdataは、int型の引数を取り、戻り値がない関数へのポ
>インターである」と読みます。
>つまり、english関数やjapanese関数のような、
>「int型の引数を取り、戻り値がない関数」を代表しています。
int型の引数を取り、戻り値がない関数を代表しているというご回答納得できました!
ありがとうございます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
- ・ゆるやかでぃべーと タイムマシンを破壊すべきか。
- ・歩いた自慢大会
- ・許せない心理テスト
- ・字面がカッコいい英単語
- ・これ何て呼びますか Part2
- ・人生で一番思い出に残ってる靴
- ・ゆるやかでぃべーと すべての高校生はアルバイトをするべきだ。
- ・初めて自分の家と他人の家が違う、と意識した時
- ・単二電池
- ・チョコミントアイス
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
[C++]継承したクラスのコンスト...
-
void func( void )について
-
C++ template operator T()
-
引数で argc argvが使用され...
-
構造体のtypedef宣言
-
警告 ”値が割り当てられていな...
-
!とキャスト
-
C言語のコンパイルエラー
-
unsigned *という宣言について
-
C++/CLIでネイティブの構造体を...
-
DWORDの実際の型は何でしょうか
-
void main (void)について、、、
-
DLLでLIBファイルが作成されない
-
ループを使用して変数に格納したい
-
C++で構造体のコピーはできても...
-
MFC C++
-
2重定義って??
-
二つ以上の値を返す関数
-
マップとアルゴリズム
-
【#define】 defineで定義した...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
visualstudio C# テキストボッ...
-
C++のfor文について
-
2重定義って??
-
long型の定数の末尾にLを付ける...
-
typedef enumの使い方を教えて...
-
main.c:7:43: warning: implici...
-
C++でboolにintの値を代入する...
-
構造体の要素すべてに対する四...
-
intとINTの違いは?
-
ハンドルされていない例外が発...
-
DDVによるメッセージの変更
-
変数の型を定義しなかった場合...
-
【#define】 defineで定義した...
-
C++ クラスをメンバにもつクラ...
-
構造体の宣言でエラーが出ます。
-
プログラムの中で別のmainを呼...
-
void func( void )について
-
エラー「invalid conversion fr...
-
関数の実体定義にヘッダファイ...
おすすめ情報