9

src1.c の内容は次のとおりです。

#include <stdio.h>
extern int w;
//int go(char); // no need to declare here. WHY????
  main(){
    char a='f';
    go(a);
    printf("%d\n", w);
}

src2.c の内容は次のとおりです。

#include <stdio.h>
int w = 99;
int go(char t){
   printf("%c\n%d\n",t,sizeof(t));
}

Linuxでコンパイルした後、ファイルでgo関数を宣言することが必須ではないのはなぜですか?src1.c

 cc src1.c src2.c; 

コンパイラは、宣言が不要になるように、ファイルgoからの関数の定義をメイン関数のコードの上に置きますか?src2.c

私はこのようにします:

#include <stdio.h>
int go(char); // need to declare here, because if not, arguments of go will be promoted to intS and they would conflict with char parameters defined in go. Error is droped!
  main(){
    char a='f';
    go(a);
} 
  int go(char t){
   printf("%c\n%d\n",t,sizeof(t));
}

したがって、プロトタイプがない場合に引数の数とタイプを渡すことが可能であると言う人は誰でも間違っています。この場合、それらは s に昇格intされますが、定義で指定されたものと一致する必要があります。


いくつかのテストを行ったところ、エラーなしでコンパイルしても正しく動作しないことがわかりました。

ソース1:

#include <stdio.h>
int go(int t){
    printf("%d\n%d\n",t,sizeof(t));
}

sr2.c:

#include <stdio.h>
int go(int); //if I omit this prototype, program outputs 1 which is far from correct answer :)
main(){ 
    double b=33453.834;
    go(b);
}

したがって、最終的に答えは未定義の動作になる可能性があります。

ありがとうマキシム・スクリディン

4

5 に答える 5

8

実際、関数を使用する前にプロトタイプを用意することは必須ではありませんが、これは C の初期の癖です。

プロトタイプが存在しない場合、コンパイラは関数に渡される、または関数によって返される実際の型をチェックできません。これは、使用法と宣言が一致しない場合に非常に悪い場合があります。

gowhengo(b);が呼び出されたときのプロトタイプが見つからない場合、コンパイラは次のプロトタイプがあると想定しますint go(<any number of arguments can be there>)。デフォルトの引数昇格は、関数呼び出しの前に引数に対して実行されます。もちろん、go別の翻訳モジュールに関数がなければリンカエラーになります。

c99 標準から:

6.5.2.2 関数呼び出し

呼び出された関数を示す式の型がプロトタイプを含まない場合、各引数に対して整数昇格が実行され、float 型の引数は double に昇格されます。これらはデフォルト引数プロモーションと呼ばれます. 引数の数がパラメーターの数と等しくない場合、動作は未定義です。関数がプロトタイプを含む型で定義されており、プロトタイプが省略記号 (, ...) で終わっているか、昇格後の引数の型がパラメーターの型と互換性がない場合、動作は未定義です。関数がプロトタイプを含まない型で定義されており、昇格後の引数の型が昇格後のパラメーターの型と互換性がない場合、次の場合を除き、動作は未定義です。

— 一方の昇格型は符号付き整数型で、もう一方の昇格型は対応する符号なし整数型であり、値は両方の型で表現可能です。

— どちらのタイプも、文字タイプまたは void の修飾または非修飾バージョンへのポインターです。

6.3.1.1 ブール値、文字、および整数

2/ int が元の型のすべての値を表すことができる場合、値は int に変換されます。それ以外の場合は、unsigned int に変換されます。これらは整数プロモーションと呼ばれます.48) 他のすべてのタイプは

アップデート:

コンパイラは、src2.c ファイルからの go 関数の定義をメイン関数のコードの上に置いて、宣言が不要になるようにしますか?

いいえ、何も入れません。上記の標準の引用では、プロトタイプは必要ないと述べています。go各ファイルは個別にコンパイルされるため、src1.c がコンパイルされると、src2.c と内部の関数定義については何もわかりません。

したがって、プロトタイプがない場合に引数の数とタイプをいくつでも渡すことができると言う人は誰でも間違っています。この場合、それらは intS に昇格されますが、定義で指定されたものと一致する必要があります。

それは可能であり、システム全体の変更後にいくつかのあいまいなバグに直面しましたが、何らかの理由で警告なしで正常にコンパイルされました (実際には未定義の動作です)。繰り返しますが、各 *.c ファイルは個別にコンパイルgoされるため、別の翻訳単位で定義された引数の数とそれらの関数の型をチェックできる方法があります。関数が指定した引数よりも多くの引数を取る場合、「未使用」の引数にはランダムなデータが入ります。引数が一致しない場合、それは未定義の動作であり、何かが起こる可能性があることを覚えておいてください。

于 2012-10-26T11:10:02.193 に答える
0

デフォルトでは

go()はになりますint go()つまり、intを返し、任意の数の引数を受け入れます。したがって、実際の関数はデフォルトの関数型と一致します。

于 2012-10-26T11:07:18.187 に答える
0

コンパイラがgo()str1.c を確認すると、関数が別の場所で定義されていると見なされます。リンカが の定義を検索するのは、リンク時のみですgo()

2つのファイルを別々にコンパイルし、それらをリンクしていると思いますが、これで問題ありません。リンク時に の定義がgo()存在するためです。

str1.c を (gcc str1.c ではなく) 個別にコンパイルしようとすると、リンカーによって見つからないgcc -c str1.cというエラーが発生します。go()

アップデート:

コンパイラによる暗黙の宣言でさえ、標準に準拠していません (C99 以降)

技術的には、コンパイラが定義を確認する前に呼び出された場合、戻り値の型に関係なく、すべての関数にプロトタイプが必要です。int を返す関数を暗黙的に宣言することはもはや有効ではなく (C89 で有効)、C99 (および C11) 以降は削除されています。

ただし、ほとんどのコンパイラは、エラーではなく、これに関する警告のみを発行します。ただし、関数プロトタイプが存在しないために一部のコンパイラがコンパイルを拒否した場合、標準に準拠していないため、文句を言うことはできません。

于 2012-10-26T11:10:22.423 に答える
0

これら2つのソースファイルを使用して1つの実行可能ファイルを作成すると、最終的な実行可能ファイルには定義がgo()含まれるため、必要ありません。

ただし、 を入れdeclarationて、header fileそのヘッダー ファイルを両方のソース ファイルにインクルードすることをお勧めします。

ここにヘッダーファイル someheader.h があります

#ifndef __SMH_
#define __SMH_

int go(char);

#endif

今、このように含めます

#include "someheader.h"

src1.cと_src2.c

于 2012-10-26T11:08:26.187 に答える
0

まず、関数宣言をヘッダー ファイルに入れる必要があります。あなたの質問への答え:
両方のファイルをコンパイルすると、リンク時に、リンカーは src2.o で go() のシンボル定義を見つけ、実行可能ファイルのシンボル参照を解決します。これが理由ですあなたのプログラムは動作します。

sizeof()コンパイル時の演算子である whichを使用しようとして1いるため、文字でそれを訴えているため、出力されます。
また、整数値 > 255 を char 変数に渡しています。これによりオーバーフローが発生し、t は 1789modulo255 を格納します。

于 2012-10-26T11:09:54.410 に答える