4

Visual Studio 2012の関数ポインターに関する質問に続いて、Cの関数ポインターへの特定の割り当ての合法性について疑問に思い始めました。

以下のコードは、割り当てられた関数が関数ポインター宣言で説明されているよりも多くのパラメーターを必要とするため、私が予想するように、警告付きでコンパイルされます( GCC 4.8)。

#include <stdio.h>

int test(int x, int y)
{
    printf("%d", x);
    printf("%d", y);
    return 0;
}

int main()
{
    int (*test_ptr)(int);
    test_ptr = test;
    test_ptr(1);
    return 0;
}

割り当てられた関数が必要とするパラメーターが少なくなるようにコードを変更した場合も、同じ警告が表示されます(GCC 4.8)。繰り返しますが、これは予想されます。

ただし、割り当てられた関数には0ではなく2つのパラメーターが必要ですが、次のコードは警告なしでコンパイルされます(GCC 4.8)。

#include <stdio.h>

int test(int x, int y)
{
    printf("%d", x);
    printf("%d", y);
    return 0;
}

int main()
{
    int (*test_ptr)();
    test_ptr = test;
    test_ptr();
    return 0;
}

鋳造はどこにも含まれていません。

誰かがこのコンパイラの動作を説明できますか?

4

2 に答える 2

9

以下:

int (*test_ptr)();

ゼロパラメータではなく、不特定の数のパラメータを取ります。

後者については、

int (*test_ptr)(void);

PS引数がゼロの2引数関数を呼び出すと、未定義の動作が発生します。

于 2013-03-27T08:09:44.637 に答える
2

関数ポインタから関数ポインタへの変換は合法です。違法なのは、実際の指定された関数と互換性のない型で関数ポインタを呼び出すことです。

C996.3.2.3パラメーター 8:

あるタイプの関数へのポインターは、別のタイプの関数へのポインターに変換され、また元に戻される場合があります。結果は元のポインタと同じになります。変換されたポインターを使用して、指定された型と互換性のない型の関数を呼び出す場合、動作は未定義です。

コンパイラが未定義の動作について正確に警告した場合は、で警告する必要がありtest_ptr();ます。ただし、コンパイラーは、すべての未定義の動作について警告したり、未定義の動作についてのみ警告したりすることを期待することはできません。


この静的アナライザー(他の人と私が取り組んでいるもの)は、すべての未定義の動作について、未定義の動作についてのみ警告するために最善を尽くします。多くの妥協が含まれていますが、この特定のケースでは:

$ cat > fp.c

int test(int x, int y)
{
    printf("%d", x);
    printf("%d", y);
    return 0;
}

int main()
{
    int (*test_ptr)();
    test_ptr = test;
    test_ptr();
    return 0;
}
$ frama-c -val fp.c
...
fp.c:13:[value] warning: Function type must match type at call site: assert(function type matches)

13行目はtest_ptr();です。

于 2013-03-27T08:13:20.680 に答える