22

コードは次のとおりです。

int func(param111)
{
    printf("%d\n", param111);
    return param111;
}

int main()
{
    int bla0 = func(99);
    int bla1 = func(10,99);
    int bla2 = func(11111110,99,10001);
    printf("%d, %d, %d\n", bla0, bla1, bla2);
}

コンパイル結果:

zbie@ubuntu:~$ gcc -Wall -g -std=c99 -O2 zeroparam.c

zeroparam.c: In function ‘func’:

zeroparam.c:2: warning: type of ‘param111’ defaults to ‘int’

実行結果:

zbie@ubuntu:~$ ./a.out

99

10

11111110

99, 10, 11111110

入力を受け入れるint func() など、パラメーターがゼロの func の場合、コードは問題ないはずです。しかし、このコードはどのようにコンパイルされ、正常に実行されるのでしょうか?

4

7 に答える 7

2

他の人が説明したように、K&R C として解釈されます。ANSI C では未定義の動作であることに注意してください。

C11 6.9.1関数定義セクション 9

可変数の引数を受け入れる関数が、省略記号表記で終わるパラメーター型リストなしで定義されている場合、動作は未定義です。

したがって、可変数引数関数は、次...のようにパラメーターとして終了する必要がありprintfます。

int printf( const char *format ,...);
于 2013-08-13T07:15:06.750 に答える
1

そのコードに対して警告が表示されない場合は、コンパイラが C99 規則を適用していないことが原因です (printf目に見える宣言なしで や任意の関数を呼び出すと、制約違反になります)。適切なオプションをコンパイラに渡すことで、おそらく少なくともいくつかの警告を受け取ることができます。gcc を使用している場合は、 を試してくださいgcc -std=c99 -pedantic -Wall -Wextra

1978 年のカーニガンとリッチーの古典的な本The C Programming Languageの第 1 版で記述された、いわゆる K&R C には、関数プロトタイプがありませんでした。(プロトタイプは、そのパラメーターの型を指定する関数宣言です。) 関数定義は依然としてそのパラメーターを (おそらく暗黙的に) 定義する必要がありましたが、宣言はそうではありませんでした。関数呼び出し) パラメータへ (関数定義内)。

間違った数や引数の型で関数を呼び出した場合に何が起こるかは、完全には明確ではありませんでした。現代的に言えば、これは未定義の動作でしたが、古いコンパイラでは通常、トリックを実行できます。

1989 ANSI C 標準 (1990 ISO C 標準として再発行) では、プロトタイプ (初期の C++ から借用) が導入されましたが、それらは必須ではありませんでした。しかし、間違った数またはタイプの引数で関数を呼び出すと、未定義の動作が発生することが明示的に述べられていました。コンパイラはそれについて警告する必要はありませんが、プログラムを実行すると、文字通り何でも実行できます。

1999 年の ISO C 標準では、"implicit int" ルールが廃止され、可視宣言なしで関数を呼び出すことは違法 (制約違反) になりましたが、古いスタイルの関数宣言と定義は引き続き許可されていました。したがって、K&R1 および C89/C90 ルールの下では、関数定義は次のようになります。

int func(param111)
{
    printf("%d\n", param111);
    return param111;
}

は有効で、param111タイプはですint。C99 ルールでは無効ですが、これは次のとおりです。

int func(param111)
int param111;
{
    printf("%d\n", param111);
    return param111;
}

はまだ合法です (2011 規格の下でも合法です)。

C99 および C11 の時点で、目に見える宣言がプロトタイプではない関数を呼び出す場合、引数を正しく取得するのは完全にあなた次第です。コンパイラは、間違った場合に警告する必要はありません。

これが、すべての関数の宣言と定義に常にプロトタイプを使用する必要がある理由です。ANSI 以前のコンパイラでコンパイルするコードを書く必要は、最近ではほとんどありません。少なくとも C89/C90 をサポートしていないコンパイラを見つけるのは困難です。

ああ、追加する必要があります

#include <stdio.h>

を呼び出しているため、ソース ファイルの先頭にprintf. C89/C90 規則ではprintf、可視宣言なしで呼び出した場合の動作は未定義です (printf可変数の引数を取るため)。C99 以降では、これは制約違反であり、コンパイル時の診断が必要です。

欠落しているパラメーター宣言について、私はつまらないことをしてきました。プログラムのわずかに変更されたバリアント:

#include <stdio.h> /* add this line */

int func(param111)
int param111;      /* add this line */
{
    printf("%d\n", param111);
    return param111;
}

int main(void)     /* add "void" */
{
    int bla0 = func(99);
    int bla1 = func(10,99);
    int bla2 = func(11111110,99,10001);
    printf("%d, %d, %d\n", bla0, bla1, bla2);
}

C90、C99、または C11 でコンパイル時の診断を必要とする規則に違反していませんが、2 番目と 3 番目の呼び出しのfunc動作は未定義です。

funcコンパイラには、呼び出しが正しくないことを警告するのに十分な情報が実際にあることに注意してください。の定義を見ただけでfunc、暗黙的に変換可能な型の引数を 1 つだけ渡さない呼び出しintは無効であることを認識しているはずです。警告は必要ありませんが、コンパイラはいつでも必要な追加の警告を表示できます。どうやら gcc (および使用しているコンパイラ) の作成者は、古いスタイルの宣言および/または定義を使用した関数の呼び出しの不一致について警告する努力をする価値がないと感じていたようです。

于 2013-08-13T15:33:13.733 に答える
1

なぜこれが機能するのかは説明できますが、コンパイラーが警告しない理由は説明できません。

引数の順序と配置場所を指定する呼び出し規約がいくつかあります。C 呼び出し規則では、副作用なしで追加のパラメーターを渡すことができます。これは、呼び出された関数ではなく、呼び出し元がそれらをクリーンアップし、それらがすべてスタックに渡されるためです。

func(10, 99) の場合、「main」は次の順序 (右から左) で値をスタックにプッシュします。

99
10

「func」は 1 つの値しか認識せず、最後から取得するため、param111 == 10.

次に、「main」は、2 つの引数がプッシュされたことを認識してそれらを取得し、スタックをクリーンアップします。

于 2013-08-13T06:49:48.770 に答える
-2

コンパイル時に警告を確認すると、次のメッセージが表示されます。

zeroparam.c:2: 警告: 'param111' の型のデフォルトは 'int' です

これは、型のない引数はデフォルトで整数になることを示しています。戻り値の型のない関数を定義するのと同じように、デフォルトintも同様です。

于 2013-08-13T06:40:31.110 に答える