関数プロトタイプ無しで、引数がfloatの場合

このQ&Aのポイント
  • 関数プロトタイプ無しで、引数がfloatの場合につまずいています。
  • 引数の型が合っているため、正常に実行できるように思われますが、結果がおかしくなってしまいます。
  • プロトタイプがない場合でも、引数をdoubleに変更すると正常に実行されます。
回答を見る
  • ベストアンサー

関数プロトタイプ無しで、引数が float の場合

またまた float がらみでつまずています。 以下のようなソース(2つのファイルに分割されています)を実行すると、 f = 2.000000 と表示されました。 test1.c で、func() のプロトタイプを書いてないのが諸悪の根源だとは思うのですが(警告もでています)プロトタイプの役割は、 ・引数の型の不整合がないようにする ・必要に応じて、仮引数と実引数の型変換を発生させる だと思います。 このソースでは(たまたまですが)引数の型もあっているので、正常に実行できそうな気がするのですが、なぜ、結果がおかしくなってしまうのでしょうか。 確かに、プロトタイプがないのは好ましくはないですが。 test1.c に、int func(float f); を追加したときには、確かに f = 0.010000 と表示されます。 また、f と func() の引数を double にしたときには、プロトタイプがなくても、正常に実行されます。 ----- test1.c ------------- int main() { float f = 0.01; func(f); return 0; } ----- test2.c ------------- #include <stdio.h> int func(float f) { printf("f = %f", f); return 0; } ----- ここまで ------------

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

既に回答にあるように、プロトタイプがない場合、float は、double に「格上げ」されてから、関数に渡されます。 これは、大昔のC……その頃まだ、プロトタイプという概念もなかった頃(先行宣言はあった)、引数においては、 ・int より小さい整数(char も)は、int に ・float は、double に それぞれ格上げされてから関数に渡されると、そう決めた(知る人ぞ知る『K&R』の時代にまでさかのぼる)というのが前段にあるわけです。 ※なぜ、わざわざそう決めたのかは、わかりませんでした。 時代が下って、プロトタイプが存在するようになった現在、プロトタイプで決められた引数の型に対して、「代入するのと同じように」型変換(同じ型なら、もちろん、変換無し)されて受け渡されるようになりました。 ※ただし、printf() のような可変引数の部分は、「省略された引数リスト」ということで、プロトタイプの場合と同じ「格上げ」が発生する。 そういうわけで、プロトタイプなしで関数を呼び出したときに、引数が float だと、格上げの影響でdouble に変換されてから関数に渡されました。 受けとるほうの関数は、float が来たと思っているので、良くないことが起こりましたとそういうわけです。 引数が double だった場合には、格上げなど無しでそのまま渡されるので、(たまたま引数があっていれば)正常に実行されたというわけです。

yumeno_kira
質問者

お礼

ありがとうございました。 「格上げ」で、いろいろ検索してしまいました。 そいうえば、printf() の場合、float は、double に変換して渡されるというのは聞いたことがありました。 同じような話だったのですね。 ありがとうございました。

その他の回答 (3)

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

ほんと~に初期のころは, 関数に引数として渡す時だけでなく, 式の中で使うときは常に ・int より小さい整数型 (char と short) は int にする ・float は double にする という変換をしてましたよね>#3. 「float で計算させるといったん double に変換してから計算するので遅くなる」という話を聞いたものです.

yumeno_kira
質問者

お礼

ありがとうございました。 そういえば、char は int にして計算されるというのは、聞いたことがありました。 float が一度 double に変換されるのも、聞いたことあるような。 それぞれ聞いたことはあるのに、なにか、頭の中でつながってなかった気がします。

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.2

プロトタイプがない場合、floatはdoubleに変換されて渡されるのが規格です。

yumeno_kira
質問者

お礼

ありがとうございました。 何かおかしくなっていると思ったのは、double に変換されていたからなのですね。 int がおかしくなるとなんとか見当も付くのですが、浮動小数点でおかしくなると、お手上げでした。

  • SaKaKashi
  • ベストアンサー率24% (755/3136)
回答No.1

>・引数の型の不整合がないようにする 不整合をチェックして一致しなければエラーにする。 >・必要に応じて、仮引数と実引数の型変換を発生させる こんな事はしません。 >f と func() の引数を double にしたときには、プロトタイプがなくても、正常に実行されます。 偶然でしょうね。 関数プロトタイプがなくて、偶然の結果を信じる人が多くて、プロトタイプ書かなくてもいいと 言い張る人は多いです。 ま、宗教のようなものですから相手にしないことです。

yumeno_kira
質問者

補足

>・必要に応じて、仮引数と実引数の型変換を発生させる たとえば、 void func(int i); というプロトタイプに対して、 func(12.3); という呼び出しは、double -> int への型変換を発生させると思うのですが。 この場合、プロトタイプがないとコンパイルではエラーがなく(警告は出ますが)実行時には見事に失敗します。

関連するQ&A

  • 関数の出力引数をクラスにするには?

    既出、または基礎の質問でしたらすみません。 ここでも他の検索エンジンでも見つけられなかったので。。。 C++です。 クラスを出力する関数を作りたいのですが、うまくできません。 ソースは以下のとおりです。問題は、プログラム下方のf1(),f2(),main()です。 長くて、そして見づらくてすみません・・・ //////////// #include<stdio.h> class test{ private: int num; float *vec; public: test(int n=1); //ctor ~test(); //dtor int getnum(){return num;} float* getvec(); void set(int,float*); void show(); }; test::test(int n){ num = n; vec = new float[n]; for(int i=0; i<n; i++) vec[i] = (float)i; } test::~test() {delete[] vec;} float* test::getvec(){ float *v; v = new float[num]; for(int i=0; i<num; i++) v[i] = vec[i]; return v; } void test::set(int n, float *v){ num = n; vec = new float[n]; for(int i=0; i<n; i++) vec[i] = v[i]; } void test::show(){ for(int i=0; i<num; i++) printf("%d: %g\n",i,vec[i]); } void f1(test &x, test &y){ int n; float *v; n = x.getnum(); v = x.getvec(); for(int i=0; i<n-1; i++) v[i] = 2.0*v[i]; y.set(n-1,v); } test f2(test x){ test y; //* int n; float *v; n = x.getnum(); v = x.getvec(); for(int i=0; i<n-1; i++) v[i] = 2.0*v[i]; y.set(n-1,v); return y; //** } void main(){ test x,y; int n = 4; float v[4] = {1.0,2.0,3.0,4.0}; printf("x:\n"); x.set(n,v); x.show(); printf("f1:\n"); f1(x,y); y.show(); printf("f2:\n"); y=f2(x); y.show(); } //////////// これを実行すると x: 0: 1 1: 2 2: 3 3: 4 f1: 0: 2 1: 4 2: 6 f2: 0: 7.38979e-38 1: 7.38979e-38 2: 6 となります。 関数f2がうまく動かない理由がわかりません。。。 出力引数にクラスはとれないのでしょうか?? よろしくお願いします。

  • *fa=(*func)(*ax)

    何度もすみません. 次のようなプログラムtest2.cを作成してみました. /* test2.c */ #include <stdio.h> float quad(float alf) { float z; z=(10.0+alf*5.0)*(10.0+alf*5.0); return z; } int main() { float* ax; float value; float* fa; float alpha; float (*func)(float); alpha=1.0; ax=&alpha; fa=&value; func=quad; value=(*func)(*ax); printf("value=%f\n",value); printf("*fa=%f\n",*fa); return 0; } これについては,先ほどのBLUEPIXYさんに助けられて,期待する答え225.0を得ることができました. そこで今度は「value=(*func)(*ax);」を「*fa=(*func)(*ax);」に変えて,test3.cを作成して実行したところ,「Segmentation fault」となりました.以下はtetst3.cです. /* test3.c */ #include <stdio.h> float quad(float alf) { float z; z=(10.0+alf*5.0)*(10.0+alf*5.0); return z; } int main() { float* ax; float* fa; float alpha; float (*func)(float); alpha=1.0; ax=&alpha; func=quad; *fa=(*func)(*ax); printf("*fa=%f\n",*fa); return 0; } test3.cの「*fa=(*func)(*ax);」は,「Numerical Recipes in C」に記載の関数mnbrakの定義内の記述に従ったのですが,何がいけないのでしょうか.よろしくお願いいたします.

  • C言語での関数の引数の受け渡しについて

    C言語での関数の引数の受け渡しについて教えてもらいたいのです。 char *p=Goo;  というポインタpがmain関数で定義され、このポインタpをある関数 void func(・・・) に渡すことは出来ますか? つまりポインタを実引数として扱うことはできるのかという事ですが・・・ int p=10; とかだったら、 void func(int test) の関数には、main関数で func(p) で仮引数testにわたせると思うんですが・・・ もし出来るようでしたら、関数の渡し方と定義の記述を教えてください。 どうか宜しくお願いします。

  • 関数を引数とする方法?

    いつもお世話になっています。 MFCでプログラムをしています。 今、任意の関数(Func1)を 積分する関数(Func2)を作っています。 現在は、被積分関数の数だけ、 積分関数(Func2)を書いているのですが、 非効率的なので、なるべく汎用性を持たせたいと 考えています。 参考書(新C言語入門シニア編)の該当個所で、 クラスでない通常の関数を引数とする場合は、 うまくいったのですが、 クラスのメンバ関数を引数とした場合、 どうしてもコンパイルエラーが 発生してしまいます。 関数Func、I及びエラーメッセージは大凡次のとおりです。今のところ、引数とする関数(Func1)の引数は、 同一個数としています。 <被積分関数の例> double ClassA::Func1 (double a){ return a * 10; } <積分関数> double ClassA::Func2 (double (*f)(double), double a, double b){ return b * f(a); } void classA::Integration() { ... Func2(Func1,a,b); ... } <エラーメッセージ> classA::Integrationの呼び出し箇所で、 「1番目の引数を double(double)からdouble(__cdecl)(double)に 変換できません」 と出ます。 double(double)の部分は合っているようなのですが、 (__cdecl)の部分が違うということまでは 分かりました。 メンバ関数であることが原因のようなので、 Func2での引数宣言を double ClassA::Func2 (double (ClassA::*f)(double), double a, double b){ return b * f(a); } に変えてみたところ、 引数受け渡しのところはクリアするのですが、 Func2(Func1,a,b); の呼び出し時に、Func2が関数ではないという エラーがでます。 アドバイス又は参考URL等を 教えていただければ助かります。 よろしくお願いします。

  • 関数の引数をvoid*でキャストする

    最近見かけたCのプログラムで、関数の引数の型は void* なのですが、その関数を使うときに 引数をvoid*でキャストしていました。 例えば、 func ( (void*) p ); こういうことです。 私の知っている知識では、 void* と 任意の型のポインタは キャストなしに相互に代入可能です。 関数の引数でも、キャストは要らないものだと思っていました。 そうすると、引数を void* でキャストするのは無意味だと思うのですが、・・・ 違うのでしょうか。処理系によるとか。 逆に、関数の引数の型がchar*などで、渡すものが void* のときはどうなのでしょうか。 下のプログラムは、関数byte_orderの引数の型はvoid*ですが、int型へのポインタ( &a )を設定しています。私の環境では、コンパイルエラーも警告もないし、動作も正常です。 #include <stdio.h> #include <string.h> void byte_order(void *vp) { char char_array[4]; strncpy(char_array, vp, 4); printf("出力します:%x %x %x %x\n", char_array[0], char_array[1], char_array[2], char_array[3]); } int main(void) { int a = 0x12345678; byte_order(&a); return 0; } このプログラムは単なる一例であって、質問はバイトオーダに関するものではありません。 また、C言語の質問であって、C++ではありません。

  • 関数のプロトタイプ宣言

    #include <stdio.h> #define N 3 #define M 4 int sum(int [][M]); (関数のプロトタイプ宣言) int main(){   ・   ・   ・ return(0); } int sum(int x[][M]){    ・    ・ } 以上のプログラムより、関数のプロトタイプ宣言や、関数内の 行列の定義でint sum(int [][M]);や int sum(x[][M]){}となっていますが なぜ、列にMだけを代入することだけでよいのでしょうか? また、教科書に関数のプロトタイプ宣言ではint sum(int [][]);だけでもよいと書かれていたのですが 実際、コンパイルしてみたところ 'int[]' 型のサイズは未知あるいはゼロとエラーがでました。 これは、コンパイラによってできるものとできないものがあるのでしょうか?? よろしくお願いします。

  • value=(*func)(*ax)

    参考にしたいある関数mnbrakの定義は,次のようになっています. void mnbrak(float *ax, float *bx, float *cx, float *fa, float *fb, float *fc, float (*func)(float)) { float ulim,u,r,q,fu,dum; *fa=(*func)(*ax); *fb=(*func)(*bx); if (*fb > *fa) { ・・・ } (中略) } ここで知りたいことは, この中の「*fa=(*func)(*ax);」という文を実行するために必要な呼び出し側のプログラム例です. そこで,次のようなプログラムを作成してみました. /* test.c */ #include <stdio.h> float quad(float alf) { float z; z=(10.0+alf*5.0)*(10.0+alf*5.0); return z; } int main() { float* ax; float value; float alpha; float (*func)(); alpha=1.0; ax=&alpha; func=quad; value=(*func)(*ax); printf("value=%f\n",value); return 0; } このプログラムtest.cを実行すると,期待する答えの225すなわち(10.0+5.0)*(10.0+5.0)にならずに,100すなわち10.0*10.0となってしまいました. デバッグモードで,上記の文「value=(*func)(*ax);」を実行するすると,ちゃんとquadへ飛んで行くのですが,引数「1.0」をquadに渡すことができずに,quad側では,alf=0.0となっていました. 私は,「ax」は変数「alpha」へのポインタなので,alphaのアドレスを保持しており,したがって「*ax」は,そのアドレスの内容,すなわち「1.0」であり,「(*func)(*ax)」によって,この「1.0」が実引数としてquadの仮引数「alph」に渡ると思ったのですが(実際quadへ飛んでいく直前では,*axは1.0になっていましたが),そうは行かなかったようなのです. 長い質問文で失礼しますが,よろしくお願いいたします.

  • 既に定義されている関数のプロトタイプがない警告

    現在、Cで作られたプログラムの単体テストをしています。 これを、以下のように、 ・testBase.h → テストのための表示とかを作成するためのもの ・progForCheck.c → これがチェックすべきもので、この中の関数をそれぞれチェック ・add.c など → main() を含んだ、チェックを実行するためのファイル として、以下のような感じで関数分のテストプログラムを作成してテストしようとしています。 テスト自体は問題ないのですが、この構成でコンパイルをすると、 警告 W8065 test.c 13: プロトタイプ宣言のない関数 'writeFotter' の呼び出し(関数 main ) 警告 W8057 test.c 15: パラメータ 'argc' は一度も使用されない(関数 main ) という警告が出ます。後者はその通りで問題ないのですが、前者の警告は、よくわかりません。 実体は3つのファイルですが、コンパイル時にはひとつのファイルになって、既に定義されている writeFotter() を呼んでいるだけなのに、「プロトタイプがない」という警告が出るのは、なぜなのでしょう。 それに、writeHeader() や、doCheck() は警告も無しです。 とりあえず、リンクも正常でちゃんと動作しているようなのですが、ちょっと気持ち悪いので質問しました。 // --------------- testBase.h ---------- #include <stdio.h> int OK = 0; int NG = 0; void doCheck(int isOK) { if (isOK) OK++; else NG++; } void writeHeader(char *title) { printf(" ------------ %s ------------\n", title); } void writeFotter() { printf(" OK = %d, NG = %d", OK, NG); if (NG == 0) printf(" ○○○○○○○○○○○○○○○○○○○\n"); else printf(" ×××××××××××××××××××\n"); } // ------------ progForCheck.c int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } // ------------ test_add.c #include "testBase.h" #include "progForCheck.c" int main(int argc, char *argv[]) { writeHeader(argv[0]); doCheck(add(1, 2) == 3); doCheck(add(2, 4) == 6); doCheck(add(8, 1) == 9); writeFotter(); return 0; }

  • 教えてください。

    普通にint main (void)   return 0 のように1つのプログラムなら出来るんですが 以下の問題になると訳が分かりません。 正しいプログラムを載せて頂くと助かります。 2つの実数a,bを入力して大きい値を返すプログラムを作れ。 ただし、プログラムはデータ入力がある間くり返されるようにする。また、a,bを引数として与え、大きい方の数を返す関数を作ること。 float Func1(float,float);このような関数プロトタイプ プログラムは2つのソースプログラムに分けて作ること。

  • プロトタイプが必要な場合

    WndProc関数の前にFunc関数を書いているから、WndProc関数の ソースがコンパイルされる時はFunc関数は既に読み込み済みで Func関数のプロトタイプは必要無いと思ったんだけど、必要なんですか? #include <windows.h> int Func(HWND); ←これは必要ですか? LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPInst, LPSTR lpstr, int n) {  … } int Func(HWND hWnd) {  … } LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {  static int i;  switch(uMsg){  case WM_CREATE:   i = Func(hWnd);   break;  … }

専門家に質問してみよう