web-dev-qa-db-ja.com

c99のfunc()とfunc(void)

void func()実際には、空のパラメーターは、すべての引数が受け入れられることを意味します。

void func(void)は引数を受け入れません。

しかし、標準C99では、次のような行を見つけます。

6.7.5.3関数宣言子(プロトタイプを含む)
14識別子リストは、関数のパラメーターの識別子のみを宣言します。 その関数の定義の一部である関数宣言子の空のリストは、その関数にパラメーターがないことを指定します。関数宣言子の空のリストパラメータの数またはタイプに関する情報が提供されないことを指定する関数の定義の一部ではありません。

標準によると、func()func(void)は同じですか?

59
anman

引用の重要な部分は、以下の太字で強調されています。

6.7.5.3関数宣言子(プロトタイプを含む)14識別子リストは、関数のパラメーターの識別子のみを宣言します。その関数の定義の一部である関数宣言子の空のリストは、その関数にパラメーターがないことを指定します。その関数の定義の一部ではないである関数宣言子の空のリストは、パラメーターの数またはタイプに関する情報が提供されないことを指定します。

したがって、パラメーターリストが本体のある関数に対して空の場合、それらは同じです。しかし、それは単なる関数の宣言です。

void function1(); // No information about arguments
void function2(void); // Function with zero arguments

void function3() {
    // Zero arguments
}

void function4(void) {
    // Zero arguments
}
21
Mats

標準によると、func()とfunc(void)は同じですか?

いいえ。func(void)は、関数がno引数を取ると言います。 func()は、関数が不特定の数の引数を取ることを示します。両方とも有効ですが、func()スタイルは廃止されているため、使用しないでください。

これは先行標準Cのアーティファクトです。C99はこれを廃止とマークしました。

6.11.6関数宣言子

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

C11の時点では、まだ陳腐化したままであり、標準から削除されていません。

8
usr

関数定義内の空のパラメーターリストは、プロトタイプが含まれておらず、パラメーターも含まれていないことを意味します。

C11§6.9.1/ 7関数定義(継続中の引用の強調は私のものです)

関数定義の宣言子は、定義される関数の名前とそのパラメーターの識別子を指定します。 宣言子にパラメータタイプリストが含まれている場合、リストはすべてのパラメータのタイプも指定します。 このような宣言子は、同じ翻訳単位で同じ関数を後で呼び出すための関数プロトタイプとしても機能します。

質問は次のとおりです。

標準によれば、func()func(void)は同じですか?

いいえ。void func()void func(void)の本質的な違いは呼び出しにあります。

C11§6.5.2.2/ 2関数呼び出しconstraintsセクション内):

呼び出された関数を示す式の型がプロトタイプを含む場合、引数の数は一致しますパラメーターの数。各引数は、その値が対応するパラメータの型の非修飾バージョンを持つオブジェクトに割り当てられるような型を持たなければなりません。

パラメーターが引数ではないことに注意してください。この関数にはパラメーターを含めることはできませんが、複数の引数を含めることができます。

空のパラメーターで定義された関数はプロトタイプを導入しないため、呼び出しに対してチェックされないため、理論的にはwhatever数の引数が提供されます。

ただし、少なくとも1つの引数を使用してこのような関数を呼び出すことは、技術的には 未定義の動作 です(Antti Haapalaの comments を参照)。

C11§6.5.2.2/ 6関数呼び出しsemanticsセクション内):

引数の数がパラメーターの数と等しくない場合、動作は未定義です。

したがって、違いはわずかです。

  • 関数がvoidで定義されている場合、制約違反(§6.5.2.2/ 2)が原因で、引数の数がパラメーターと(型とともに)一致しない場合、コンパイルされません。このような状況では、適合コンパイラからの診断メッセージが必要です。
  • 空のパラメータで定義されている場合、mayまたはmayコンパイルしないかもしれません(適合コンパイラからの診断メッセージの要件はありません) 、ただし、そのような関数をcallするのはUBです。

例:

#include <stdio.h>

void func1(void) { puts("foo"); }
void func2()     { puts("foo"); }

int main(void)
{
    func1(1, 2); // constraint violation, it shouldn't compile
    func2(3, 4); // may or may not compile, UB when called
    return 0;
}

最適化コンパイラ は、そのような場合に引数を切り捨てることがあることに注意してください。たとえば、これはClangが上記のコードをコンパイルする方法です(func1の呼び出し)-01 SysV ABI呼び出し規約によるx86-64の場合:

main:                                   # @main
        Push    rax          ; align stack to the 16-byte boundary
        call    func2        ; call func2 (no arguments given)
        xor     eax, eax     ; set zero as return value
        pop     rcx          ; restore previous stack position (RSP)
        ret