C言語における関数宣言/定義競合時の処理について述べる(VS 2010,VC 6)

7044 ワード

まず,本稿で論じた例はC++言語ではなくC言語を用いる.
サンプルを使用して、この問題を分析します.
例1
#include 

void hello(int a);
void hello(int a, int b);

void hello(int a)
{
	printf("hello, %d
", a); } void main() { hello(1); }

この例ではhello()関数を2回宣言し,1回定義した.
VS 2010のコンパイル結果は、
xx.c(4): warning C4031: second formal parameter list longer than the first list xx.c(7): warning C4029: declared formal parameter list different from definition
VC 6のコンパイル結果は、
xx.c(4) : warning C4031: second formal parameter list longer than the first listxx.c(7) : warning C4029: declared formal parameter list different from definition xx.c(13) : error C2198: 'hello' : too few actual parameters
例2
#include 

void hello(int a);
void hello(int a, int b);

void hello(int a)
{
	printf("hello, %d
", a); } void hello(int a, int b); void main() { hello(1); }

例2スケールサブ1は関数宣言(赤い部分)を1つ増やした.
この例ではhello()関数を3回宣言し,1回定義した.
VS 2010のコンパイル結果は、
xx.c(4): warning C4031: second formal parameter list longer than the first list xxc(7): warning C4029: declared formal parameter list different from definition xx.c(11): warning C4031: second formal parameter list longer than the first list xx.c(15): error C2198: 'hello' : too few arguments for call
VC 6のコンパイル結果は、
xx.c.c(4) : warning C4031: second formal parameter list longer than the first listxx.c.c(7) : warning C4029: declared formal parameter list different from definitionxx.c(15) : error C2198: 'hello' : too few actual parameters
----------------------------------------------------------------------------------------------------------------------------------------
この2つの例を比較すると,以下の分析が可能である.
1)関数呼び出しにおいて、提供されるパラメータの数が関数プロトタイプ(関数プロトタイプの決定、後述)で要求されるパラメータの数よりも小さい場合、「too few actual parameters」は、VS 2010およびVC 6のいずれにおいてもエラーとして報告される.
2)複数の関数宣言、定義が存在する場合、関数プロトタイプをどのように決定しますか?
(1)の解析結果から,関数呼び出しに基づいて関数プロトタイプを確認することができ,エラーが報告されない場合hello(int a)は関数プロトタイプであり,エラーが報告された場合hello(int a,int b)こそ関数プロトタイプである.
具体的な分析過程はもう議論しないで、私がざっと得た結論を話します.
複数の関数の宣言と定義が同時に存在する場合、
VS 2010では、関数プロトタイプは、すべての宣言(定義を含む)の最後の1つによって決定される.
VC 6では、関数プロトタイプは、すべての宣言(定義を含まない)の最後の1つによって決定される.
3)実際の2)の解析で問題があり,「最後の1つ」は,どのようにして最後の1つであり,関数呼び出し後に関数宣言があるのか.関数呼び出し後の関数宣言後に関数呼び出しはありますか?どのように処理しますか?
インスタンス解析を続行する.
例3
#include 

void hello(int a);
void hello(int a, int b);

void hello(int a)
{
	printf("hello, %d
", a); } void hello(int a, int b); void main() { hello(1); } void hello(int a); void callhello() { hello(1); }

例3では、main()関数の後に、関数宣言と関数呼び出しが追加される.
VS 2010のコンパイル結果は以下の通り(warningを省略)
xx.c(15): error C2198: 'hello' : too few arguments for call
VC 6のコンパイル結果は以下の通り(warningを省略)
xx.c(15) : error C2198: 'hello' : too few actual parameters
VS 2010とVC 6の処理は一致しており、1回目のhello()呼び出しエラーが表示され、2回目はエラーが表示されない.
これは、関数呼び出しが正しいかどうかを確認するために使用される関数のプロトタイプが異なることを示します.
関数呼び出しを検証するための関数プロトタイプは,ソースファイル,関数呼び出し前,その関数のすべての宣言によって決定されると容易に推測できる.
検証を続行できます.
例4
#include 

void hello(int a);
void hello(int a, int b);

void hello(int a)
{
	printf("hello, %d
", a); } void hello(int a, int b); void main() { hello(1); } void hello(int a, int b); void callhello() { hello(1); }

VS2010:
xx.c(15): error C2198: 'hello' : too few arguments for call xx.c(22): error C2198: 'hello' : too few arguments for call
VC6:
xx.c(15) : error C2198: 'hello' : too few actual parameters xx.c(22) : error C2198: 'hello' : too few actual parameters
最後の2番目のhello()呼び出し前の関数宣言を変更し、2番目のhello()呼び出しもエラーを報告しました.
上記の結論を結びつけると、次のようになります.
複数の関数の宣言と定義が同時に存在する場合、
関数呼び出しを検証するための関数プロトタイプは、ソースファイル、関数呼び出し前、その関数のすべての宣言の最後の1つによって決定されます.
VS 2010では、すべての宣言に定義が含まれ、VC 6では、すべての宣言に定義は含まれません.
PS:1つの関数呼び出し前にこの関数の定義しか存在しない場合、関数呼び出しのチェックは必ず関数定義の関数プロトタイプを使用することを強調します.
しかし,同時に,関数プロトタイプは関数呼び出しのコンパイルチェックだけでなく,リンクの問題にも関連していることが分かった.
しかし、C言語では、この点については、あまり考慮する必要はありません.リンクはシンボルテーブルに依存するため,シンボルテーブルでは関数のシンボルは関数プロトタイプによって決定される.
しかしC言語にはname manglingメカニズムがなく,関数の記号は実際には関数名のみで決定される.
同じ例です.
例5
//main.c
#include 

void hello(int a);

void main()
{
	hello(1);
}
//sub.c
#include 

void hello(int a, int b)
{
	printf("hello, %d %d
", a, b); }
)接尾辞名はCで、VS 2010とVC 6を使用してコンパイルしても正常で、エラーはありません.
2)接尾辞名をC++に変更して再コンパイルすると、
VS 2010エラー:
main.obj : error LNK2019: unresolved external symbol "void __cdecl hello(int)"(?hello@@YAXH@Z) referenced in function _main xxxx.exe : fatal error LNK1120: 1 unresolved externals
VC 6エラー:
main.obj : error LNK2001: unresolved external symbol "void __cdecl hello(int)"(?hello@@YAXH@Z) xxxx.exe : fatal error LNK1120: 1 unresolved externals
この点から,C言語の関数プロトタイプに対する検査メカニズムは,C++の検査メカニズムには天然に及ばない.
最後に、おもしろいことをもう二つ話します.
1、旧式スタイル関数宣言
例6
#include 


void hello(int a, int b)
{
	printf("hello, %d, %d
", a, b); } void hello(); void main() { hello(1, 2); }

このプログラムはコンパイルされますか?
我々の結論によれば,VC 6とVS 2010は,hello(1,2)呼び出しをチェックする際にhello()を用いており,エラーを報告すべきである.
しかし、コンパイルしてみると、プログラムは正常にコンパイルされています.WHY?
ここでは古い声明の問題に関連しています.
void hello();古い宣言(関数の戻りタイプのみを与える)と見なすことも、パラメータのない関数の新しいスタイルのプロトタイプと見なすこともできます.
もちろん旧式声明はとっくにごみの山の中のものですが、コンパイラは旧式のスタイルの互換性を保証しなければならないので、hello()は旧式のスタイルの声明と理解されます.
SO.....void hello();関数呼び出しのチェックには影響しません.
上記のすべての例でvoid hello()を勝手に追加してみました.コンパイル結果には影響しません.
2、多すぎる関数パラメータ
例7
#include 

void hello(int a)
{
	printf("hello, %d
", a); } void main() { hello(1, 2); }

上はどんな状況ですか??関数パラメータが多すぎて、コンパイル結果を見てみましょう.
VS2010:(warning level3)
XX.C(10): warning C4020: 'hello' : too many actual parameters
VC6:(warning level3)
XX.C(10) : warning C4020: 'hello' : too many actual parameters
少なくとも:(warning level 3)では、WARNINGのみで、エラーは報告されていません.
運転しても、正常です.
では、接尾辞の名前をc++?VS2010:
1>ClCompile: 1>  main.cpp 1>f:\prj_cs\c\try_0819\try_0819\main.cpp(10): error C2660: 'hello' : function does not take 2 arguments
VC6:
d:\my documents\test\test0818\test.c(10) : warning C4020: 'hello' : too many actual parameters Linking... test.obj : error LNK2005: _main already defined in main.obj main.obj : error LNK2001: unresolved external symbol "void __cdecl hello(int)"(?hello@@YAXH@Z) Debug/test0818.exe : fatal error LNK1120: 1 unresolved externals
違います.1つのコンパイルが間違っていて、1つのリンクが間違っていますが、結局は間違っています.
これによって、Cのいくつかの天然能力が不足していることが証明された~~~.
具体的な基准がどのように决まっているのかについては、后で基准を検讨する时间がある...