web-dev-qa-db-ja.com

C関数構文、パラメーターリストの後に宣言されたパラメータータイプ

私は比較的Cに慣れていません。これまで見たことのない関数構文の形式に出くわしました。パラメータータイプはそのパラメーターリストの後に定義されます。誰かがそれが典型的なC関数の構文とどのように異なるかを私に説明できますか?

例:

int main (argc, argv)
int argc;
char *argv[];
{
return(0);
}
74
John

これはパラメーターリストの古いスタイルの構文であり、引き続きサポートされています。 K&R Cでは、型宣言を省略することもでき、デフォルトではintになります。つまり.

main(argc, argv)
char *argv[];
{
    return 0;
}

同じ機能になります。

63
Ferruccio

また興味深いのは、プロトタイプのある関数とプロトタイプのない関数の呼び出し規約の違いです。古いスタイルの定義を検討してください。

void f(a)
 float a; {
 /* ... */
}

この場合、呼び出し規約では、すべての引数が関数に渡される前に昇格されます。したがって、fdoubleを受け取るが、パラメーターのタイプがfloat(完全に有効)である場合、コンパイラーは、実行前にdoubleをfloatに変換するコードを発行する必要があります関数の本体。

プロトタイプを含めると、コンパイラーはそのような自動プロモーションを行わなくなり、渡されたデータは割り当てのようにプロトタイプのパラメーターの型に変換されます。したがって、以下は正当ではなく、未定義の動作になります。

void f(float a);
void f(a)
  float a; {

}

この場合、関数の定義は、送信されたパラメーターをdouble(プロモートされたフォーム)からfloatに変換します。これは、定義が古いスタイルだからです。ただし、関数にはプロトタイプがあるため、パラメーターはfloatとして送信されました。矛盾を解決する選択肢は次の2つです。

// option 1
void f(double a);
void f(a)
  float a; {

}

// option 2
// this declaration can be put in a header, but is redundant in this case, 
// since the definition exposes a prototype already if both appear in a 
// translation unit prior to the call. 
void f(float a); 

void f(float a) {

}

オプション2は、古いスタイルの定義を前もって取り除くため、選択できる場合は優先されるべきです。関数のこのような矛盾する関数タイプが同じ変換ユニットに表示される場合、コンパイラは通常ユーザーに通知します(ただし、必須ではありません)。そのような矛盾が複数の翻訳単位で見られる場合、エラーはおそらく気付かれず、バグの予測が困難になる可能性があります。これらの古いスタイルの定義は避けてください。

これはso-caller K&Rスタイルまたはold-style宣言です。

この宣言は、かなりが現代の宣言と異なることに注意してください。 K&R宣言は、関数にprototypeを導入しません。つまり、パラメーターのタイプを外部コードに公開しません。

10
AnT

関数定義の古い構文は引き続き機能しますが(コンパイラーに問い合わせると警告が表示されます)、それらを使用しても関数プロトタイプは提供されません。
関数プロトタイプがない場合、コンパイラーは関数が正しく呼び出されるかどうかをチェックしません。

#include <stdio.h>
int foo(c)
int c;
{ return printf("%d\n", c); }

int bar(x)
double x;
{ return printf("%f\n", x); }

int main(void)
{
    foo(42); /* ok */
    bar(42); /* oops ... 42 here is an `int`, but `bar()` "expects" a `double` */
    return 0;
}

プログラムを実行すると、私のマシンの出力は

$ gcc proto.c
$ gcc -Wstrict-prototypes proto.c
proto.c:4: warning: function declaration isn’t a prototype
proto.c:10: warning: function declaration isn’t a prototype
$ ./a.out
42
0.000000
4
pmg

違いはありません。Cの関数宣言の古い構文であるということだけです。これはANSI以前に使用されていました。 80年代から友人に提供する予定がない限り、このようなコードを書かないでください。また、暗黙の型の仮定に依存しない(別の答えが示唆するように)

4
aviraldg

まったく同じですが、昔ながらのファッションです。おそらく、古いレガシーコードであることがわかります。

1
eyalm

古いかどうかにかかわらず、私は何が古いか、そして何がそうであるかを論じるでしょう。振り返ってみると、古いプログラムは今でもメモリリークなしで動作しますが、これらの「新しい」プログラムは頻繁に失敗する傾向があります。ここにトレンドがあります。

おそらく彼らは、実行可能な本体を持つ構造体として関数を見ました。この謎を解決するには、ここでASMの知識が必要です。

編集、引数名をまったく指定する必要がないことを示すマクロが見つかりました。

#ifndef OF /* function prototypes */
#  ifdef STDC
#    define OF(args)  args
#  else
#    define OF(args)  ()
#  endif
#endif

#ifndef Z_ARG /* function prototypes for stdarg */
#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
#    define Z_ARG(args)  args
#  else
#    define Z_ARG(args)  ()
#  endif
#endif

以下に使用例を示します。ライブラリは zlib-1.2.11 です。

ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));

したがって、私の2番目の推測は関数のオーバーロードに関するものです。それ以外の場合、これらの引数は役に立ちませんでした。 1つの具体的な関数、および現在は同じ名前の無数の関数。

0
Ako