◎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.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型の引数を取り、戻り値がない関数を代表しているというご回答納得できました!
ありがとうございます。
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.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.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.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.7ベストアンサー
- 回答日時:
int foo ();
という関数の宣言があった場合,
C : fooはint型の値を返す関数
C++ : fooは「引数をとらず」int型の値を返す関数
と,関数宣言の引数として何も書かなかった時の意味合いが異なってきます。
また,Cでは任意の関数へのポインタ型の値を任意の関数へのポインタ型に変換できますが,
C++ではこの変換は存在しません。
# キャストと変換は異なります。
このため,C++ではそもそもコンパイル時にエラーとなります。
ご回答ありがとうございます。
>Cでは任意の関数へのポインタ型の値を任意の関数へのポインタ型に変換で
>きますが,
>C++ではこの変換は存在しません。
># キャストと変換は異なります。
上記の内容とても納得できました!
確かにC++でvoid型ポインタによるキャスト変換は出来ました。
「int foo( );」と引数を空にすると、C++では引数を勝手にvoidとしてしまうから実行出来ないという認識は合っているでしょうか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# プログラムの時、フローチャートはどうなりますか?図でお願いします。 int main(void) { 1 2022/10/01 22:45
- C言語・C++・C# C 言語の Gauss Jordan 法について 2 2022/12/28 11:16
- C言語・C++・C# プログラミングペーパーテスト 次の問題の実行結果を答えろ #include int x[ ] = { 1 2022/06/16 21:49
- C言語・C++・C# プログラミングのペーパーテスト 実行結果を表示せよ #include <stdio.h> int h 1 2022/07/09 15:27
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- C言語・C++・C# c言語でユーザ関数を利用して複素数のべき乗と絶対値の数列を計算するプログラムが作りたいです。 3 2023/01/29 22:13
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
long型の定数の末尾にLを付ける...
-
C言語の<ctype.h>のisspace()に...
-
構造体の要素すべてに対する四...
-
C++のfor文について
-
[C++]メンバ関数の仮引数について
-
visualstudio C# テキストボッ...
-
C++の(左辺値)参照を参照渡し
-
C++でboolにintの値を代入する...
-
typedef enumの使い方を教えて...
-
関数の実体定義にヘッダファイ...
-
main.c:7:43: warning: implici...
-
intとINTの違いは?
-
コンパイルすると error C1083 ...
-
ハンドルされていない例外が発...
-
値を返り値に返すのと参照渡し...
-
識別子が定義されていませんと...
-
2重定義って??
-
void func( void )について
-
【#define】 defineで定義した...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
DWORDの実際の型は何でしょうか
-
visualstudio C# テキストボッ...
-
2重定義って??
-
long型の定数の末尾にLを付ける...
-
C++のfor文について
-
typedef enumの使い方を教えて...
-
関数の実体定義にヘッダファイ...
-
変数の型を定義しなかった場合...
-
ハンドルされていない例外が発...
-
C++でboolにintの値を代入する...
-
main.c:7:43: warning: implici...
-
プログラムの中で別のmainを呼...
-
【#define】 defineで定義した...
-
void func( void )について
-
構造体の要素すべてに対する四...
-
C++の(左辺値)参照を参照渡し
-
構造体の宣言でエラーが出ます。
-
main()とint main(void)の違い
-
intとINTの違いは?
-
エラー「invalid conversion fr...
おすすめ情報