• ベストアンサー

関数のアドレスをコピーすることについて

◎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++ではそもそもコンパイル時にエラーとなります。

muffler
質問者

お礼

ご回答ありがとうございます。 >Cでは任意の関数へのポインタ型の値を任意の関数へのポインタ型に変換で >きますが, >C++ではこの変換は存在しません。 ># キャストと変換は異なります。 上記の内容とても納得できました! 確かにC++でvoid型ポインタによるキャスト変換は出来ました。 「int foo( );」と引数を空にすると、C++では引数を勝手にvoidとしてしまうから実行出来ないという認識は合っているでしょうか?

その他の回答 (6)

回答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

muffler
質問者

お礼

ご回答ありがとうございます。 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型を宣言すると、「引数を持たない関数」です。

muffler
質問者

お礼

ご回答ありがとうございます。 >void型の変数*aではなく、void*型の変数aです。 >void*型は、「型は未定な変数の先頭アドレス」を指します。 >int*型は、「型がintな変数の先頭アドレス」を >double*型は、「型がdoubleな変数の先頭アドレス」をそれぞれ指します。 上記のご回答の先頭アドレスを渡すという考え理解できました。 ありがとうございます。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

「void 型」の変数を宣言することはできません. 当然, 関数の仮引数でもアウト. あと, void (*putdata)( ); と宣言すると, putdata は「値を返さない関数へのポインタ」となります. 一方 void (*putdata)(int *); では「int * を引数にとり値を返さない関数へのポインタ」です. 関数へのポインタや関数のプロトタイプ宣言において () の中を空にすると「引数に対する情報 (引数の数や個々の引数の型) を一切与えない」ということを意味します. 関数を定義するときには, これとは異なり「空 = 引数を持たない」と解釈されます.

muffler
質問者

お礼

ご回答ありがとうございます。 >「void 型」の変数を宣言することはできません とご回答してくれましたが、 No.3の回答者様が『何かのポインタ型』という宣言の仕方が『void*』になります。と回答してくれたのですが、「int (*get_sign) (void *a);」はint型にもdouble型にも対応できる、void型という変数*aと認識したのですが、この認識は間違っているのでしょうか? ◎1、2の(1)、(2)「void (*putdata)( );」「int (*get_sign) ( );」のように引数を書かないと2つのプログラムとも実行できたのが、いまいち理解できません。 教えていただけると嬉しいです。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答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* といった具合に解釈されます

muffler
質問者

お礼

ご回答ありがとうございます。 >『何かのポインタ型』という宣言の仕方が『void*』になります >場合によって実際の型が変わります >get_sign = iget_sign; >とすれば引数は int* >get_sign = dget_sign; >とすれば引数は double* 以上のご回答納得できました! ◎1について、◎2のように(1)の部分を「void (*putdata)(void d);」として何かの変数型dとするとエラーが出てしまいました。この表現はだめなのでしょうか? しかし、「void (*putdata)( );」とすると何故か実行できました。 何故なのか教えてもらえると嬉しいです。 後、「void (*putdata)( );」のようの引数を空にすると、引数はvoidと認識されるのでしょうか?

muffler
質問者

補足

自分でもいろいろと勉強してみました。 例えば、「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++で実行可能にするにはどうすればよいのでしょうか? 教えていただければ嬉しいです。

  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.2

普通の関数プロトタイプでの引数の型指定については理解されているのでしょうか? それと同じですが。 もしそもそも関数プロトタイプについてわからないのであればもっと簡単なパターンで考えると良いかと。

muffler
質問者

お礼

ご回答ありがとうございます。 関数プロトタイプについては理解しています。 関数へのポインタの利用でつまづいてしまいました。 関数のアドレスをコピーするということで、アドレスが代入される関数プロトタイプの引数の表現の仕方がちょっと理解できていない状態です。

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.1

>◎1の「putdata=english;」と関数のアドレスをコピーするにおいて、(1)の部分の引数の「(int d)」がどういう意味があるか分かりません。 (1)の部分は、「putdataは、int型の引数を取り、戻り値がない関数へのポインターである」と読みます。 つまり、english関数やjapanese関数のような、 「int型の引数を取り、戻り値がない関数」を代表しています。 よって、 >putdata=english; >putdata(a); この2行で english(a); と同じ意味を持ちます。

muffler
質問者

お礼

>(1)の部分は、「putdataは、int型の引数を取り、戻り値がない関数へのポ >インターである」と読みます。 >つまり、english関数やjapanese関数のような、 >「int型の引数を取り、戻り値がない関数」を代表しています。 int型の引数を取り、戻り値がない関数を代表しているというご回答納得できました! ありがとうございます。

関連するQ&A

  • よく分からない関数?がある

    プログラミングが良く分かっていないため、このプログラムを読みきる事ができませんでした。 #include <stdio.h> int (*get_sign)(); int iget_sign(int *a); int dget_sign(double *a); main(){ int sgn, idt=5; //intを2つ作成。idtは5 double fdt = -3.234; //ダブル型のfidを作成、数値は-3.234 get_sign = iget_sign; //不明:get_sigin sgn = get_sign(&idt); //get_siginにidtの数値を渡してsgnにいれてる printf("*date:%d sgn:%d\n",idt ,sgn); get_sign = dget_sign; sgn = get_sign(&fdt); printf("*data:%lf sgn:%d\n", fdt, sgn); } 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) return 0; else if(*a > 0.0) return 1; else return -1; } 数値が0なら0を、プラスなら1をマイナスなら-1を返すプログラムだと言うことは分かります。 しかし、2行目の「int (*get_sign)();」や10行目の「get_sign = iget_sign; //不明:get_sigin」が良く分かりません。 ぜひアドバイスをお願いします。

  • scanf関数の戻り値について

    --------------------------------------- #include<stdio.h> int main(void) { int dt; while(scanf("%d",&dt)==1){ if(dt==0){ printf("0は入力しないでください\n"); puts(""); } else if(dt<0){ dt=-dt; printf("入力値の絶対値は「%d」です\n",dt); puts(""); } else{ printf("入力値の絶対値は「%d」です\n",dt); puts(""); } } return 0; } --------------------------------------- 以上のプログラムについて疑問があります。 scanf関数の戻り値が1の間、繰り返すというもので、入力の時に整数入力ですが、あえて実数である1.1を入れたとします。 scanfの戻り値は1で、dtには整数部の1だけ設定されていたので、これでもうまくいくのかと思ったのですが、次の入力はscanfの戻り値が0になっていて出来ませんでした。 何故0になっているのかわかりません。 入力バッファに何か残ってしまっているということなのでしょうか? 以上教えていただけると嬉しいです。

  • strtol関数をmalloc()関数を使用して次のソースプログラムを

    strtol関数をmalloc()関数を使用して次のソースプログラムを修正しなさいを言われました。 どなたか詳しい方よろしくお願いします。 #include <limits.h> #include <ctype.h> #include <errno.h> #include <stdio.h> int _space_sign(const char *s, const char **endptr); int _space_sign(const char *s, const char **endptr) { int sign ; while (isspace((unsigned char)*s)) ++s; sign = 0; switch (*s) { case '-': sign = -1; // fall through case '+': ++s; break; } *endptr = s; return sign; } long int strtolong(const char * s, char ** endptr, int base) { int c; int sign = _space_sign(s, (const char**)&s); long result; if (s[0] == '0') { ++s; if ((s[1] | 0x20) == 'x') { if (base == 0 || base == 16) { ++s; base = 16; } } else if (base == 0) base = 8; } else if (base == 0) base = 10; result = 0; for (; c = tolower((unsigned char)*s), isdigit(c) || ('a' <= c && c <= 'v'); s++) { int d ; if( isdigit(c) ) d= c - '0' ; else d = c - 'a' + 10; if (d >= base) break; if (result > (LONG_MAX - d - sign) / base) { errno = ERANGE; result = sign ? LONG_MIN : LONG_MAX; } else { result = result * base + d; } } if (endptr != NULL) *endptr = (char*)s; if (sign != 0) result = -result; return result; } int main(void) { char s[128], *e; long n; int base; printf("何進数で変換しますか。"); scanf("%d", &base); printf("変換する数値を入力してください。"); scanf("%s", s); n = strtolong(s, &e, base); if (errno != ERANGE) { printf("変換数値=%ld\n", n); if (*e != '\0') { printf("変換不可能部分=%s\n", e); printf("%d文字目の\'%c\'が変換不可\n", e-s+1, *e); } } else if (n == LONG_MAX) printf("long値で表現できる値を上回りました。\n"); else if (n == LONG_MIN) printf("long値で表現できる値を下回りました。\n"); return 0; }

  • C++の関数で

    Visual C++で6の4乗を求めるプログラムを作ろうとしたのですがうまくいきません。どこが間違っているか教えていただけないでしょうか? #include "stdafx.h" int get; bekijyo(int,int); void main(void) { int number1,number2; int kekka; number1=6; number2=4; kekka=get; bekijyo(number1,number2); printf("%dの%dは%dです。); } int getbekijo(int x,int y) { int z; if(y==1) return(x); z=x; getbekijyo(x,y-1); return(z); }

  • サブ関数、メイン関数

    C言語の質問です! ランダムに九九の計算を出題して、キーボードから答えを入力し、正解かどうかを判定するプログラムをつくりたいです。出題の部分をサブ関数、判定の部分をメイン関数として作成する場合、どのようなプログラムの書き方になるのか、教えてください! #include<stdio.h> #include<stdlib.h> #include<process.h> void kakezan(void); void answer(void); void hanrei(void); int a,b,c; int main() { printf("やめるときは、100と入力してください。\n");   while(1){ kakezan(); answer(); hantei(); } return 0; } void kakezan(void) { a=rand() %10; b=rand() %10; printf("%d×%d=",a,b); } void answer(void) { scanf("%d",&c); if(c==100) exit(0); } void hantei(void) { if(c==a*b) printf(">>正解\n"); else printf(">>不正解\n"); printf("\n"); return; } 一応考えてみたのですが、 ちゃんと、判定→メイン  出題→サブ関数になっているのかなとおもって・・・   このプログラムであっているのでしょうか??

  • 長方形、円、三角形の計算するプログラムでエラーがでます

    タイトルの通りなんですがエラーがでます>< コンパイラはできたのですが、実行して二つ目の入力するとこで、入力したあと止まります。 どこがおかしいのでしょうか? #include <stdio.h> int sikaku(void); int en(void); int main(void) { char ch; int a,b; printf("円(A) 長方形(B) 三角形(C)\n"); printf("入力してください:"); ch = getche(); if(ch == 'C'){ printf("\n底辺を入力してください:"); scanf("%d ",a); printf("高さを入力してください:\n"); scanf("%d",b); printf("%dです",a * b); } else if(ch == 'B') sikaku(); else if(ch == 'A') en(); return 0; } int en(void) { int a; float f; printf("\n半径を入力してください:"); scanf("%d",a); printf("円周率を入力してください:"); scanf("%f",f); printf("%fです",a * a * f); return 0; } int sikaku(void) { int a,b; printf("\n縦を入力してください:"); scanf("%d",a); printf("横を入力してくさい:\n"); scanf("%d",b); printf("dです",a * b); return 0; }

  • &と配列とアドレスについて

    &と配列とアドレスの関係性についての質問です。 以下のような処理を書くと、「a」「&a」「&a[0]」は全て同じ 値をさしてしまいます。 「a」と「&a[0]」が同じなのはわかりますが、「&a」も 同じというのは、なぜでしょうか。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ int main(void) {  int a[1];  printf("%d\n",a);  printf("%d\n",&a);  printf("%d\n",&a[0]);  return 0; } ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  • void型ポインタについて

    -------------------------------- #include<stdio.h> void uni_disp(void *p,int typ); int main() { int idt=123456; double ddt=56.789; char ss[]="abcdef"; uni_disp(&idt,'I'); uni_disp(&ddt,'D'); uni_disp(ss,'S'); uni_disp("STRING",'S'); return 0; } void uni_disp(void *p,int typ) { if(typ=='I'){ printf("%d\n",*(int *)p); } else if(typ=='D'){ printf("%f\n",*(double *)p); } else if(typ=='S'){ printf("%s\n",(char *)p); } } ----------------------------------- 以上のプログラム等で、void型ポインタをint型ポインタ、double型ポインタとみなす場合の、「*(int *)p」や「*(double *)p」の表記がどういう仕組みになっているか分かりません。 「*(int)p」などはエラーが出るのですが、やはり表記の意味を理解していないため何故か分かりません。 「*(int *)p」などの表記を分解して教えていただけると嬉しいです。

  • 分数を表示するプログラム(長文です)

    (整数値)aの値と(整数値)bの値をキーボードから入力して、そこから、a/bという分数を作るプログラムを書きたいと思います。(符号や約分も考えた形にする) 僕は、ヒントを参考に以下のようなプログラムを書いたのですが、ある条件の下では、正しく動きません。 ・aの値が負の場合 ・aもbも負の値の場合 など・・・ #include <stdio.h> #include <math.h> int main ( void ) { int a,b; int k,min,sign; printf("a=?");scanf("%d",&a); printf("b=?");scanf("%d",&b); if (a*b<0) sign = -1; else sign = 1; a = sign*a ; b = sign*b; if (abs(a)>abs(b)) min = b; else min = abs(a); for ( k=2 ; k<=min ; k++){ if (min%k == 0){ a = a/k; b = b/k; } } printf("a/b = %d",a); if (b != 1) printf("/%d\n",b); else printf("\n"); return 0; } これをどのように修正すれば、正確な答えが出るのでしょうか? 教えてください。

  • 関数のコピー

    以下のようなソースがあります。 していることは、char配列に関数をコピーしています。それを関数ポインタに変換して、実行しています。 自作の場合はできるのですが、 MessageBoxをコピーすると以下(字数の関係上無理でした。)のようにメモリ内容がなっており、そのままコピーしたつもりですが、若干異なっております。 どのようにすればMessageBoxがじっこうできるのか, 教えていただきたく質問しました。 よろしくお願いします。 mb,code3が該当分です。 //ソース #include<stdio.h> #include<memory.h> #include<windows.h> int func(int cnt) { return cnt*10; } int func2(int cnt,int cnt2) { return cnt+cnt2; } char *func3() { return "Hello"; } int main() { int (*fa)(int); //MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaotion,UINT uType); int (*fm)(int,int); int (*mb)(HWND,LPCTSTR,LPCTSTR,UINT); char *(*hl)(void); /*fa=func; printf("%d",fa(5)); getchar(); */ char code[12800]; char code2[12800]; char code3[80000]; char code4[12800]; memset(code,0,12800); memset(code2,0,12800); memset(code3,0,80000); memset(code4,0,12800); memcpy(code,(char *)func,12800); memcpy(code2,(char *)func2,12800); memcpy(code3,(char *)MessageBox,80000); memcpy(code4,(char *)func3,12800); fa=(int(*)(int))(void *)code; fm=(int(*)(int,int))(void *)code2; mb=(int(*)(HWND,LPCTSTR,LPCTSTR,UINT))(void *)code3; hl=(char *(*)(void))(void *)code4; printf("%d\n",fa(3)); printf("%d\n",fm(3000,10)); printf(hl()); MessageBox(NULL,"","",MB_OK); mb(NULL,"","",MB_OK); getchar(); return 0; }

専門家に質問してみよう