私は誰かのCコードに出会ったばかりで、なぜコンパイルされているのかについて混乱しています。理解できない点が2つあります。
まず、関数プロトタイプには、実際の関数定義と比較してパラメーターがありません。次に、関数定義のパラメーターに型がありません。
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
なぜこれが機能するのですか?いくつかのコンパイラでテストしましたが、正常に動作します。
他のすべての答えは正しいですが、 完了
関数は次の方法で宣言されます。
return-type function-name(parameter-list,...) { body... }
return-typeは、関数が返す変数の型です。これは、配列型または関数型にすることはできません。 指定されない場合、intが想定されます。
function-nameは、関数の名前です。
parameter-listは、関数がコンマで区切ったパラメーターのリストです。 パラメータが指定されていない場合、関数は何も受け取らず、空の括弧セットまたはキーワードvoidで定義する必要があります。パラメータリスト内の変数の前に変数タイプがない場合、intが想定されます。配列と関数は関数に渡されませんが、自動的にポインターに変換されます。リストが省略記号(、...)で終了している場合、パラメーターの数は設定されていません。注:省略記号を使用する場合、ヘッダーstdarg.hを使用して引数にアクセスできます。
また、完全性のために。 C11仕様6:11:6から (ページ:179)
空の括弧を使用した関数宣言子の使用(プロトタイプ形式のパラメータ型宣言子ではない)は廃止された機能です。
Cでは、func()
は、任意の数の引数を渡すことができることを意味します。引数が必要ない場合は、func(void)
として宣言する必要があります。指定しない場合、関数に渡す型はデフォルトでint
になります。
int func();
は、C標準がなかった日、つまりK&R C(1989年、最初の「ANSI C」標準が発行された年の前)の時代からの廃止された関数宣言です。
K&R Cにはプロトタイプはありませんがあり、キーワードvoid
はまだ発明されていないことに注意してください。できることは、コンパイラに関数の戻り値の型を伝えることだけでした。 K&R Cの空のパラメーターリストは、「指定されていないが固定された」数の引数を意味します。 Fixedは、毎回same引数の数で関数を呼び出す必要があることを意味します(variadicprintf
のような関数ではなく、呼び出しごとに数と型を変えることができます) )。
多くのコンパイラがこの構成を診断します。特にgcc -Wstrict-prototypes
は、「関数宣言はプロトタイプではありません」と通知します。これは特筆すべきものです。looksはプロトタイプのようです(特にC++に毒されている場合!)。これは、古いスタイルのK&R C戻り値型宣言です。
経験則:空のパラメーターリスト宣言を空のままにしないでください。具体的にはint func(void)
を使用してください。これにより、K&Rの戻り値の型宣言が適切なC89プロトタイプに変わります。コンパイラは幸せ、開発者は幸せ、静的チェッカーは幸せです。しかし、C++の^ W ^ Wfondによって誤解されるのは、外国語のスキルを行使しようとするときに余分な文字を入力する必要があるためです。
int
と想定されます。これを渡すビルドは、設定された警告/エラーレベルに欠けていると考えますが、実際のコードを許可することには意味がありません。
K&Rスタイルの関数宣言と定義です。 C99標準(ISO/IEC 9899:TC3)から
セクション6.7.5.3関数宣言子(プロトタイプを含む)
識別子リストは、関数のパラメーターの識別子のみを宣言します。関数の定義の一部である関数宣言子の空のリストは、関数にパラメーターがないことを指定します。 その関数の定義の一部ではない関数宣言子の空のリストは、パラメーターの数または型に関する情報が提供されないことを指定します(両方の関数型が「古いスタイル」の場合、パラメーター型は比較されない。)
セクション6.11.6関数宣言子
空の括弧を持つ関数宣言子(プロトタイプ形式のパラメーター型宣言子ではない)の使用は、廃止された機能です。
セクション6.11.7関数定義
個別のパラメーター識別子と宣言リスト(プロトタイプ形式のパラメータータイプと識別子宣言子ではない)を使用した関数定義の使用は、廃止された機能です。
古いスタイルとはK&Rスタイル
例:
宣言:int old_style();
定義:
int old_style(a, b)
int a;
int b;
{
/* something to do */
}
関数の戻り値の型とパラメータリストに型が指定されていない場合、Cはint
を想定しています。このルールについては、奇妙なことに従うことのみが可能です。
関数定義は次のようになります。
int func(int param) { /* body */}
そのプロトタイプを作成する場合
int func(int param);
プロトタイプでは、パラメーターのタイプのみを指定できます。パラメーターの名前は必須ではありません。そう
int func(int);
また、パラメーターのタイプを指定していないが、名前としてint
がタイプとして想定されている場合。
int func(param);
さらに先へ進むと、以下も機能します。
func();
コンパイラは、int func()
を記述するときにfunc()
を想定します。ただし、func()
を関数本体に入れないでください。それは関数呼び出しになります
@Krishnabhadraで述べたように、他のユーザーからの以前のすべての応答には正しい解釈があり、いくつかのポイントのより詳細な分析を行いたいだけです。
ANSI-CのようなOld-Cでは、「ntyped形式パラメータ」は、8ビットMPUで作業レジスタまたは命令深度機能(シャドウレジスタまたは命令累積サイクル)の次元を取ります。 64ビットアーキテクチャが-m32のようなオプションをコンパイルすることを選択する場合、16ビットMPUではint16であるため、int16などです。
高レベルではより簡単な実装のように見えますが、複数のパラメーターを渡す場合、制御次元データ型ステップでのプログラマーの作業はより困難になります。
他の場合では、一部のマイクロプロセッサアーキテクチャでは、ANSIコンパイラがカスタマイズし、この古い機能の一部を活用してコードの使用を最適化し、これらの「型なしの正式なパラメーター」の場所を作業レジスタの内外で強制的に動作させ、今日では「volatile」と「register」を使用してもほぼ同じです。
ただし、最新のコンパイラーでは、2種類のパラメーター宣言を区別しないことに注意してください。
Linuxでのgccを使用したコンパイルの例:
いずれの場合でも、プロトタイプのステートメントはローカルでは役に立たない。なぜなら、このプロトタイプへの参照がパラメーターなしで呼び出されることはないからである。 「型なしの仮パラメータ」を使用してシステムを使用する場合、外部呼び出しに対して、宣言的なプロトタイプデータ型の生成に進みます。
このような:
int myfunc(int param);
パラメーターの種類に関しては、すでに正しい答えがここにありますが、コンパイラーからそれを聞きたい場合は、いくつかのフラグを追加してみてください(とにかくフラグはほとんど常に良い考えです)。
gcc foo.c -Wextra
を使用してプログラムをコンパイルする:
foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]
奇妙なことに、-Wextra
はclang
に対してこれをキャッチしません(何らかの理由で、おそらく上記の歴史的な理由から-Wmissing-parameter-type
を認識しません)が、-pedantic
はします:
foo.c:5:10: warning: parameter 'param' was not declared,
defaulting to type 'int' [-pedantic]
int func(param)
^
1 warning generated.
プロトタイプの問題については、上記で再度説明したように、int func()
は、int func(void)
として明示的に定義しない限り、任意のパラメーターを参照します。
foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here
またはclang
で:
foo.c:5:5: error: conflicting types for 'func'
int func(param)
^
foo.c:3:5: note: previous declaration is here
int func(void);
^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
int bla = func(10);
~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.
関数宣言にパラメーターがない場合、つまり空の場合、指定されていない数の引数を取ります。引数を取らないようにする場合は、次のように変更します。
int func(void);
だからこそ、私は通常、人々にコードをコンパイルするように勧めます:
cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition
これらのフラグはいくつかのことを強制します。
これらのフラグは、多くのオープンソースプロジェクトでもデフォルトで使用されています。たとえば、FreeBSDは、MakefileでWARNS = 6を使用してビルドするときにこれらのフラグを有効にします。