(実際の関数定義と比較して)パラメーターのない関数がコンパイルされるのはなぜですか?


388

私は、なぜそれがコンパイルされているのか混乱している誰かのCコードに出くわしました。わからないことが2点あります。

まず、関数プロトタイプには、実際の関数定義と比較してパラメーターがありません。次に、関数定義のパラメーターに型がありません。

#include <stdio.h>

int func();

int func(param)
{
    return param;
}

int main()
{
    int bla = func(10);    
    printf("%d", bla);
}

なぜこれが機能するのですか?いくつかのコンパイラでテストしましたが、問題なく動作します。


77
それはK&R Cです。完全な関数プロトタイプができる前に、1980年代にこのようなコードを書きました。
hughdbrown、

6
gccは-Wstrict-prototypesint func()およびint main():xc:3:警告の両方に対して警告を出します:関数宣言はプロトタイプではありません。あなたは宣言する必要がありますmain()としてmain(void)だけでなく、。
イェンス、

3
@Jensなぜ質問を編集したのですか?ポイントを逃したようです...
dlras2

1
私は暗黙のintを明示的にしました。それはどのように要点を逃していますか?ポイントはがint func();と互換性がある理由ですint func(arglist) { ... }
イェンス、

3
@MatsPeterssonこれは間違っています。C99 5.1.2.2.1はあなたの主張に明確に矛盾し、引数なしのバージョンはであると述べていますint main(void)
イェンス、

回答:


271

他のすべての答えは正しいですが、完了のためだけです

関数は次のように宣言されます。

  return-type function-name(parameter-list,...) { body... }

return-typeは、関数が返す変数の型です。これは、配列型または関数型にすることはできません。指定しない場合、intが想定されます。

function-nameは、関数の名前です。

parameter-listは、関数がコンマで区切ったパラメーターのリストです。パラメーターが指定されていない場合、関数は何も受け取らないため、括弧の空のセットまたはキーワードvoidを使用して定義する必要があります。パラメータリストの変数の前に変数の型がない場合、intが想定されます。配列と関数は関数に渡されませんが、自動的にポインターに変換されます。リストが省略記号(、...)で終了している場合、パラメーターの数は設定されていません。注:省略記号を使用する場合、ヘッダーstdarg.hを使用して引数にアクセスできます。

また、完全を期すために。C11仕様6:11:6から(ページ:179)

空の括弧付き関数宣言子の使用(しないプロトタイプ形式パラメータ型宣言子)は廃止予定事項です


2
「パラメーターリストで変数の前に変数の型がない場合、intが想定されます。」これはあなたが提供したリンクに表示されますが、標準のc89、c99では見つかりません...別のソースを提供できますか?
godaygo 2018年

「パラメーターが指定されていない場合、関数は何も受け取らず、括弧の空のセットで定義する必要があります」と、Tony The Lionの答えは矛盾しますが、Tony The Lionの答えが難しい方法であることを知りました。
jakun

160

Cではfunc()、任意の数の引数を渡すことができます。引数が必要ない場合は、として宣言する必要がありますfunc(void)。指定しない場合、関数に渡す型はデフォルトでになりintます。


3
実際には、デフォルトでintになる単一の型はありません。実際、すべての引数のデフォルトはintです(戻り値の型も含みます)。呼び出すのfunc(42,0x42);まったく問題ありません(すべての呼び出しで2つの引数の形式を使用する必要があります)。
イェンス、

1
Cでは、たとえば変数を宣言するときのように、指定されていない型がデフォルトでintになることはよくあります。unsigned x; 変数xはどの型ですか?符号なし整数である
bitek '19 / 12/19

4
暗黙のint 一般的でした。それは確かにもはやないとC99でそれがCから削除されました
イェンス

2
それは、この答えは空のパラメータリストで関数プロトタイプに適用されますが、ことは注目に価値があるかもしれない、空のパラメータリストで定義機能します。
自閉症の

「指定されていない型がデフォルトでintになっている」ではない@mnemonicflow; unsignedは、と同じタイプの別名unsigned intです。
ホッブズ2015

58

int func();C標準が存在しなかった時代、つまりK&R Cの時代からの廃止された関数宣言(1989年より前、最初の「ANSI C」標準が発行された年)です。

K&R Cにはプロトタイプなく、キーワードvoidがまだ発明されていないことに注意してください。できることは、関数の戻り値の型をコンパイラに伝えることだけでした。K&R Cの空のパラメーターリストは、「指定されていないが固定された」数の引数を意味します。固定とは、毎回同じ数の引数を使用して関数を呼び出す必要があることを意味します(のような可変関数printfでは、呼び出しごとに数と型が異なる可能性があります)。

多くのコンパイラはこの構造を診断します。特に、gcc -Wstrict-prototypes「関数宣言はプロトタイプではない」と表示されます。プロトタイプのよう(特にC ++に毒されている場合)、そうではありません。これは古いスタイルのK&R C戻り型宣言です。

経験則:空のパラメーターリスト宣言を空のままにしないでくださいint func(void)。具体的に使用してください。これにより、K&R戻り型宣言が適切なC89プロトタイプに変わります。コンパイラは幸せ、開発者は幸せ、静的チェッカーは幸せです。^ W ^ WfondのC ++の誤解を招く人たちは、外国語スキルを練習しようとするときに余分な文字を入力する必要があるため、うんざりするかもしれません:-)


1
これをC ++に関連付けないでください。空の引数リストはパラメーターがないことを意味するのは常識であり、世界中の人々は、40年ほど前にK&Rの人が犯した間違いに対処することに興味がありません。しかし、委員会の専門家たちは、常識に反する選択をC99、C11に引きずり込み続けています。
pfalcon 2014年

1
@pfalcon 常識はかなり主観的です。ある人にとっての常識は、他の人にとって明らかな狂気です。プロのCプログラマーは、空のパラメーターリストが指定されていないが固定数の引数を示すことを知っています。これを変更すると、多くの実装が壊れる可能性があります。無言の変更を回避するための委員会の責任は、私見で間違ったツリーを樹立しています。
イェンス

53
  • 空のパラメーターリストは「任意の引数」を意味するので、定義は間違っていません。
  • 欠落しているタイプはと見なされますint

これを渡すビルドは、構成された警告/エラーレベルが不足していると考えますが、これが実際のコードを許可することに意味はありません。


30

それのK&Rスタイルの関数宣言と定義。C99標準(ISO / IEC 9899:TC3)から

セクション6.7.5.3関数宣言子(プロトタイプを含む)

識別子リストは、関数のパラメーターの識別子のみを宣言します。関数の定義の一部である関数宣言子の空のリストは、関数にパラメーターがないことを指定します。関数の定義の一部ではない関数宣言子の空のリストは、パラメーターの数またはタイプに関する情報が提供されないことを指定します。(両方の関数タイプが「古いスタイル」の場合、パラメータータイプは比較されません。)

セクション6.11.6関数宣言子

括弧が空の関数宣言子(プロトタイプ形式のパラメーター型宣言子ではない)の使用は、廃止された機能です。

セクション6.11.7関数の定義

個別のパラメーターIDと宣言リスト(プロトタイプ形式のパラメータータイプとID宣言子ではない)を持つ関数定義の使用は、廃止された機能です。

古いスタイルはK&Rスタイルを意味します

例:

宣言: int old_style();

定義:

int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}

16

Cはint、関数の戻り値の型とパラメーターリストに型が指定されていない場合を想定しています。このルールについてのみ、以下の奇妙なことが可能です。

関数定義は次のようになります。

int func(int param) { /* body */}

そのプロトタイプを書く場合

int func(int param);

プロトタイプでは、パラメーターのタイプのみを指定できます。パラメータの名前は必須ではありません。そう

int func(int);

また、パラメータタイプを指定しない場合でも、名前intはタイプと見なされます。

int func(param);

さらに遠くに行くと、以下も機能します。

func();

コンパイラーはint func()、ユーザーがいつ書き込むかを想定していますfunc()。ただしfunc()、関数本体の中に入れないでください。それは関数呼び出しになります


3
空のパラメーターリストは、暗黙のint型とは関係ありません。int func()はの暗黙の形式ではありませんint func(int)
John Bartholomew、

11

@Krishnabhadraで述べたように、他のユーザーからの以前のすべての応答には正しい解釈があり、いくつかのポイントについてより詳細な分析を行いたいだけです。

ANSI-CのようなOld-Cでは、「型なし仮パラメーター」は、8ビットMPUでは、作業レジスターまたは命令の深さ機能(シャドウレジスターまたは命令の累積サイクル)の次元を取り、16ビットではint16になります。 MPUなどはint16などになります。64ビットアーキテクチャが-m32のようなオプションをコンパイルすることを選択した場合に備えます。

高レベルでの実装はより単純に思えますが、複数のパラメーターを渡す場合、プログラマーによる制御次元データ型のステップでの作業は、より厳しいものになります。

他の場合では、一部のマイクロプロセッサアーキテクチャでは、カスタマイズされたANSIコンパイラがこの古い機能の一部を利用してコードの使用を最適化し、これらの「型なし仮パラメータ」の場所を強制的に作業レジスタ内または作業レジスタ外で機能させました。 「volatile」と「register」を使用してもほぼ同じです。

ただし、最新のコンパイラでは、2種類のパラメータ宣言を区別しないことに注意してください。

Linuxでのgccを使用したコンパイルの例:

main.c

main2.c

main3.c  
いずれの場合も、このプロトタイプへのパラメーター参照がないと呼び出しがないため、プロトタイプのステートメントをローカルで使用しても意味がありません。「型なし仮パラメーター」でシステムを使用する場合、外部呼び出しでは、宣言的なプロトタイプデータ型の生成に進みます。

このような:

int myfunc(int param);

5
私は手間をかけて画像を最適化するために、画像のバイト単位のサイズを最小限に抑えました。写真を見ると、生成されたコードに違いがないことが一目でわかります。私はコードをテストして、問題について理論化するだけでなく、それが主張するものの実際のドキュメントを生成しました。しかし、すべてが同じように世界を見るわけではありません。私がコミュニティにもっと多くの情報を提供して、あなたをそんなに悩ましてきた私の試みがひどく申し訳ありません。
RTOSkit

1
未指定の戻り値の型は常にintであると確信しています。これは通常、ワークレジスタサイズまたは16ビットのいずれか小さい方です。
スーパーキャット2013

@supercat +1完璧な控除、あなたは正しいです!これはまさに私が上記で説明したものであり、特定のアーキテクチャ用にデフォルトで設計されたコンパイラは、常に問題のCPU / MPUのワークレジスタのサイズを反映します。したがって、組み込み環境では、さまざまな再入力戦略があります。リアルタイムOS、コンパイラレイヤー(stdint.h)、または移植性レイヤー。他の多くのアーキテクチャで同じOSを移植可能にし、CPU / MPU固有のタイプ(int、long、long longなど)を整列させることによって取得します。汎用システムタイプu8、u16、u32。
RTOSkit 2013

@supercatオペレーティングシステムまたは特定のコンパイラによって提供されるコントロールタイプがない場合、開発時に少し注意を払う必要があり、「型なし割り当て」がすべてアプリケーション設計と一致するようにしてから、「」を見つけることに驚きます。 「16ビット整数」であり、「32ビット整数」ではありません。
RTOSkit 2013

@RTOSkit:あなたの答えは、8ビットプロセッサのデフォルトの型がInt8になることを示唆しています。私はPICmicroブランドアーキテクチャ用のいくつかのCっぽいコンパイラを思い出しましたが、そうでしたが、Cの標準にリモートで似ているintものが、範囲内のすべての値を保持することができない型を許可したとは思いません- 32767〜+32767(注-32768は不要)であり、すべてのchar値を保持できます(つまり、charが16ビットの場合、符号付きであるか、それintよりも大きい必要があります)。
スーパーキャット2013

5

パラメーターの種類に関しては、ここで既に正しい答えがありますが、コンパイラーからそれを聞きたい場合は、いくつかのフラグを追加してみてください(フラグはとにかくほとんど常に良いアイデアです)。

gcc foo.c -WextraI get を使用してプログラムをコンパイルする:

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 doesnt 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.

3

関数宣言にパラメータがない場合、つまり空の場合、指定されていない数の引数を取ります。引数を取らないようにするには、次のように変更します。

int func(void);

1
「関数プロトタイプにパラメータがない場合」これは関数プロトタイプではなく、関数宣言だけです。
effeffe

@effeffeが修正しました。この質問が多くの注目を集めることに気づかなかった;)
PP

3
「関数プロトタイプ」は、「関数宣言」の代わりの(おそらく旧式の)式ではありませんか?
Giorgio

@ジョルジオIMO、どちらも正しいです。「関数プロトタイプ」は「関数定義」と一致する必要があります。おそらく私はeffeffeが質問の宣言を意味し、私が何を意味するのかが私の答えにあると思います。
PP

@Giorgioいいえ、関数宣言は戻り値型の値、識別子、およびオプションのパラメーターリストによって行われます。関数プロトタイプは、パラメーターリストを使用した関数宣言です。
effeffe

0

これが、私が一般的に人々に彼らのコードをコンパイルすることを勧める理由です:

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

これらのフラグはいくつかのことを強制します:

  • -Wmissing-variable-declarations:最初にプロトタイプを取得せずに非静的関数を宣言することは不可能です。これにより、ヘッダーファイルのプロトタイプが実際の定義と一致する可能性が高くなります。または、公開する必要のない関数にstaticキーワードを追加することを強制します。
  • -Wstrict-variable-declarations:プロトタイプは引数を適切にリストする必要があります。
  • -Wold-style-definition:関数定義自体も引数を適切にリストする必要があります。

これらのフラグは、多くのオープンソースプロジェクトでもデフォルトで使用されます。たとえば、MakefileでWARNS = 6を使用してビルドする場合、FreeBSDではこれらのフラグが有効になっています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.