C言語関係掲示板

過去ログ

No.1236 プロトタイプ宣言の無い関数呼び出しの警告

[戻る] [ホームページ]
No.16521

プロトタイプ宣言の無い関数呼び出しの警告
投稿者---ビギナー(2004/09/03 11:11:24)


下記のプログラムをBCC32でコンパイルしました。
15行目の (*func[n])( n ); で、プロトタイプ宣言の無い関数呼び出し
という警告が出ます。
プロトタイプ宣言を、どのように書けば警告が出なくなるのか教えてください。

#include <stdio.h>

void func0( void );
void func1( int n );

void (*func[])() = { func0, func1 };

void main()
{
int     n;

        scanf("%d", &n) ;

        if (n >= 0 && n <= 1)
                (*func[n])( n );
        else
                printf( "\Error\n" );
}

void func0( void )
{
        printf( "\nfunc0()\n" );
}

void func1( int n )
{
        printf( "\nfunc1(%d)\n", n );
}




No.16522

Re:プロトタイプ宣言の無い関数呼び出しの警告
投稿者---ぽこ(2004/09/03 11:32:02)


>プロトタイプ宣言を、どのように書けば警告が出なくなるのか教えてください。
funcという変数は、「引数を取らず値を返さない関数」の配列と定義していますが、
実際に呼び出すときに、int型の引数を渡しているから警告が出るのではないでしょうか?

#むしろ何故コンパイラがエラーと判断しないのか不思議。
#またfunc0()とfunc1()では型が違うので、どの道コンパイルが通らないと思いますが。。


No.16524

Re:プロトタイプ宣言の無い関数呼び出しの警告
投稿者---ビギナー(2004/09/03 11:41:12)


レス、ありがとうございます。

>#またfunc0()とfunc1()では型が違うので、どの道コンパイルが通らないと思いますが。。

コンパイルも実行も出来ております。Windows2000 BCC32 を使用しています。
警告を、消したいのです。



No.16523

解決しました。
投稿者---ビギナー(2004/09/03 11:36:08)


解決できました。すみませんでした。

#include <stdio.h>

void func0( void );
void func1( int n );

typedef void (*FUNC)(int n);

void (*func[])(int n) = { (FUNC)func0, func1 };

void main()
{
int     n;

        scanf("%d", &n) ;

        if (n >= 0 && n <= 1)
                (*func[n])( n );
        else
                printf( "\Error\n" );
}

void func0( void )
{
        printf( "\nfunc0()\n" );
}

void func1( int n )
{
        printf( "\nfunc1(%d)\n", n );
}




No.16526

Re:解決しました。
投稿者---Kelly(2004/09/03 12:04:15)


void (*func[])(int n) = { (FUNC)func0, func1 };

関数アドレスのキャストは、言語仕様的に未定義だったと思う。
たまたま動いてるだけじゃない?

いかがでしょうか>識者の方


No.16527

Re:解決しました。
投稿者---nop(2004/09/03 12:34:02)


>解決できました。すみませんでした。
>void func0( void );
>void func1( int n );
>typedef void (*FUNC)(int n);
>void (*func[])(int n) = { (FUNC)func0, func1 };

全く解決になっていません。
これは、

「間違った事をしているよ」

と警告を出してくれているコンパイラに、

「うるさい!だまれ!」

と、口にガムテープを貼り付け、
喋れなくしている様なものです。


関数呼び出しの時の構文が違う関数を関数テーブルにして、
無理矢理、同じ構文で呼び出そうと言うのは、
間違って行為でしかありません。

関数テーブルでテーブル化出来る関数は、
同じ構文で呼び出せる関数だけです。


No.16528

レスありがとうございました。
投稿者---ビギナー(2004/09/03 13:14:18)


初心者なので、よく判っていないのですが、Cの関数呼び出しの場合、
引数をスタックに積んでから、関数を呼び出し、関数から戻って、このスタックを戻すのではなかったかと思います。
そこで、最も引数の多い関数で呼び出せば、引数の少ない関数では、使用しないだけですから、問題なしと、考えました。
もし、これが間違いであるなら、printfは、どのように実装するのか、とても不思議です。


No.16529

Re:レスありがとうございました。
投稿者---nop(2004/09/03 13:56:19)


>引数をスタックに積んでから、関数を呼び出し、関数から戻って、このスタックを戻すのではなかったかと思います。

それは、処理系/環境に依存します。
必ずそうなっている訳ではありません。

また、ここで問題となっているのは、

>void func0( void );
>void func1( int n );

と言う、異なる型の二つの関数を、

>void (*func[])(int n);

と言う、関数テーブルに設定している事です。

「func1()」は、テーブルの型と一致しますが、
「func0()」は、テーブルの型と一致しません。

これは、「char」に「int」を格納したり、
「int」に「double」を格納したりする様なものです。


この場合は、「func1()」は引数を使用しなくても、
「void func0( int n );」などとするとよいかと思います。


No.16531

Re:レスありがとうございました。
投稿者---ビギナー(2004/09/03 14:22:33)


何度も、レス、誠にありがとうございます。

>この場合は、「func1()」は引数を使用しなくても、
>「void func0( int n );」などとするとよいかと思います。

これは、まずいです。
変数nを一度も使用していないと、警告を出されてしまい。
当初の目的の警告を消せますが、新たな警告が出ます。
この変数を、何かを、代入するのは無意味と思えますので、
誠にありがたい助言ではございますが、却下とさせていただきます。

デバッガで、確認しても問題ないのコードが出ていますので、終了と考えています。
どうも、ありがとうございました。


No.16535

Re:レスありがとうございました。
投稿者---Kelly(2004/09/03 14:56:29)


>変数nを一度も使用していないと、警告を出されてしまい。
>当初の目的の警告を消せますが、新たな警告が出ます。
>この変数を、何かを、代入するのは無意味と思えますので、

#define UNREFFERENCED(var) var
みたいなコードもよく見かけますし、
まともなコンパイラなら最適化で消してくれますよ。

また、
void func0( int ){}
として引数に名前を付けなければVC, BCC, GCCなどのコンパイラでは
警告が出なかったと思います。




No.16541

ありがとうございます。
投稿者---ビギナー(2004/09/03 15:34:21)


>void func0( int ){}
>として引数に名前を付けなければVC, BCC, GCCなどのコンパイラでは
>警告が出なかったと思います。

BCC32を使用しておりますが、残念ながらエラーとなってコンパイルできませんでした。
助言ありがとうございました。



No.16542

Re:ありがとうございます。
投稿者---REE(2004/09/03 15:37:53)


>>void func0( int ){}
>>として引数に名前を付けなければVC, BCC, GCCなどのコンパイラでは
>>警告が出なかったと思います。
>
>BCC32を使用しておりますが、残念ながらエラーとなってコンパイルできませんでした。
>助言ありがとうございました。

具体的にどんなエラーになったのでしょうか?
プロトタイプ宣言の方を書き直し忘れたりしていませんか?



No.16545

エラーメッセージです。
投稿者---ビギナー(2004/09/03 15:54:32)


>具体的にどんなエラーになったのでしょうか?
>プロトタイプ宣言の方を書き直し忘れたりしていませんか?

Borland C++ 5.6.4 for Win32 Copyright (c) 1993, 2002 Borland
prot.c:
エラー E2287 prot.c 23: 1 番目のパラメータに名前がない(関数 func0 )
*** 1 errors in Compile ***

上記のような、メッセージが出て、コンパイルできませんでした。
プロトタイプ宣言は、間違っていないと思いますが.....



No.16530

Re:レスありがとうございました。
投稿者---おでん(2004/09/03 14:00:31)


>初心者なので、よく判っていないのですが、Cの関数呼び出しの場合、
>引数をスタックに積んでから、関数を呼び出し、関数から戻って、このスタックを戻すのではなかったかと思います。

確かにそういった実装だと思いますが、それを当てにするのは
あまりすることではないと思います。a,b,cのパラメータが
c,b,aと積まれる処理系がないともいえませんし?

>そこで、最も引数の多い関数で呼び出せば、引数の少ない関数では、使用しないだけですから、問題なしと、考えました。

何に対して「問題無し」でしょうか?

>もし、これが間違いであるなら、printfは、どのように実装するのか、とても不思議です。
実装方法がぜんぜん違います。「可変数引数の関数は、少なくとも一つの
固定された引数を持っているのが前提です。」
va_list、va_start、va_arg、va_end
あたりを見たほうがいいと思います。


No.16532

Re:レスありがとうございました。
投稿者---ビギナー(2004/09/03 14:32:28)


レス、誠にありがたく感謝しております。

>確かにそういった実装だと思いますが、それを当てにするのは
>あまりすることではないと思います。a,b,cのパラメータが
>c,b,aと積まれる処理系がないともいえませんし?

スタックの積み方ですが、関すの右側から、積んでいき最後に関数を呼び出す順番は、絶対に変わらないと思います。なぜなら、可変個数の引数を持つ関数の場合、幾つ積んだかは、呼ばれた関数側から知る方法が無いため、第一引数が、最後のスタックポインターの次に無いと一番最初の引数が、何か判らないと思うのですが、いかがでしょうか?

>何に対して「問題無し」でしょうか?

呼ばれた関数で使用していないため、バグになることはありえません。
逆に、引数2個の関数を呼び出せば、問題は出るはずです。

警告が消せるようになったので、ありがたく思っております。
_(_^_)_


No.16534

Re:レスありがとうございました。
投稿者---あかま(2004/09/03 14:54:36)


>スタックの積み方ですが、関すの右側から、積んでいき最後に関数を呼び出す順番は、絶対に変わらないと思います。
例えば、スタックで実現していない環境があったら?
とか考え出すとキリがないですが。
ANSIとかの規格で決まっているなら、規格準拠のコンパイラを使っている限り”絶対に変わらない”といっても大丈夫でしょう。
規格書が手元にないのでなんともいえませんが。

>呼ばれた関数で使用していないため、バグになることはありえません。
>逆に、引数2個の関数を呼び出せば、問題は出るはずです。
これは大嘘ですね。未定義の動作を書いた時点でバグです。
バグなのですから警告消すより、未定義の動作を書かないほうが数億倍マシなプログラムといえます(個人的に)。
もっとも、警告は大抵、未定義の動作に対してだったりしますが。
警告なし&未定義なしが良質のプログラムだと思います。

>警告が消せるようになったので、ありがたく思っております。
少なくともキャストで黙らせるのはまずいです。
「キャスト 黙らせる C言語」
でググればその理由がわかると思います。


No.16536

Re:レスありがとうございました。
投稿者---monkey(2004/09/03 15:14:53)


>> この場合は、「func1()」は引数を使用しなくても、
>> 「void func0( int n );」などとするとよいかと思います。

> これは、まずいです。
> 変数nを一度も使用していないと、警告を出されてしまい。
> 当初の目的の警告を消せますが、新たな警告が出ます。
> この変数を、何かを、代入するのは無意味と思えますので、
> 誠にありがたい助言ではございますが、却下とさせていただきます。

> 警告が消せるようになったので、ありがたく思っております。

警告さえ出なければ、バグがあっても良いとおっしゃっているように見えます。
1.間違いだが、警告は出ない
2.警告は出るが、正しい
どちらが良いですか?


No.16567

Re:解決しました。
投稿者---Sciggepy(2004/09/03 22:37:47)


>これは、
>「間違った事をしているよ」
>と警告を出してくれているコンパイラに、
>「うるさい!だまれ!」
>と、口にガムテープを貼り付け、
>喋れなくしている様なものです。

それは、

bcc32 -w- ...

でしょう。



No.16543

どうもありがとうございました。
投稿者---ビギナー(2004/09/03 15:39:49)


レスをくださった皆さん、どうもありがとうございました。
問題は解決しましたので、この質問は終了とさせてください。
もちろん実際の問題は、サンプルほど、簡単な物ではないのですが、うまく動作しております。環境がWindowsで、bccとかgccでしか動作しないのかもしれませんが、とりあえずノントラブルで、確実に動いております。
皆さんには、心より感謝しております。
今後とも、よろしくお願いします。
_(_^_)_



No.16544

Re:どうもありがとうございました。
投稿者---kelly(2004/09/03 15:52:45)


いいというなら別に止めはしませんが、
個人的には素直に分岐処理するのがいい気はします。

どうしてもテーブル引きしたくて、既存の関数が直せないなら、関数アドレスの配列の方は特定のインターフェイスに統一しておいて、
個別処理へのアダプタを(面倒でも)書くかなぁ。
で、BCC に合わせて UNREFFERENCED とかを定義する。

# BCC は通らなかった..?手元の VC, GCC は通るのですが、
# BCC は今手元にないので記憶違いしてたかも。すみません。


No.16548

アドバイス、ありがとうございます。
投稿者---ビギナー(2004/09/03 16:21:44)


実際の問題では、関数が256個もあるので、これをswitchで処理するのは、いかにも汚い感じがするし、見通しが悪いため、ここの関すにして、テーブル処理すると実にエレガントに数行でまとまるため、今回採用しました。
皆さんが、おっしゃらんとするところも、よく判りますが、どうしたら警告が止まるのか質問しました。提示しましたソースコードには、バグは無いと思います。
どうもありがとうございました。



No.16550

Re:アドバイス、ありがとうございます。
投稿者---tetrapod(2004/09/03 16:24:08)


fastcall を使ったり stdcall を使ったりするとてきめんにバグるでしょうね。
VC++ の cdecl な場合には(その実装の特性上)問題ないと思われますが。



No.16570

Re:アドバイス、ありがとうございます。
投稿者---RAPT(2004/09/03 23:14:45)


# 感想文。
エレガント
いい響きですよね。諸刃の響きを持つ悪魔の言葉です。

今はいいですが、後に他人もしくは自分自身がそのソースに手を入れる
必要ができたときに、意味不明で混乱してしまったり、仕様変更などで
苦しむ種になったりします。

小手先のテクニックで、ついつい環境依存、たまたま、偶然動いている
ようなプログラムを素晴らしいものだと自画自賛したくなってしまうかも
しれません。

私の場合だったら、もしどうしても、関数のインターフェイスを統一
しなければならないとしたら、デザインパターン Adapterパターン
で実装すると思います。