調整されたパラメーターに関連する C99 の未定義の動作

C 標準からの最初の引用は正しくありません。のようですね

complete という単語を省略したということです .

たとえば、その定義と同じ型ではない関数宣言では、

のような不完全なオブジェクト型を指定できます。
void f( size_t, size_t, int [][*] );

この関数宣言では、配列要素のサイズが不明であるため、3 番目のパラメーターの宣言は完全なオブジェクト型ではありません。

ここにデモ用プログラムがあります

#include <stdio.h>

void f( size_t, size_t, int [][*] );

void f( size_t m, size_t n, int a[][n] )
{
    for ( size_t i = 0; i < m; i++ )
    {
        for ( size_t j = 0; j < n; j++ )
        {
            a[i][j] = n * i + j;
        }
    }
}

void g( size_t, size_t, int [][*] );

void g( size_t m, size_t n, int a[][n] )
{
    for ( size_t i = 0; i < m; i++ )
    {
        for ( size_t j = 0; j < n; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}

int main(void) 
{
    size_t m = 2, n = 3;
    int a[m][n];
    
    f( m, n, a );
    g( m, n, a );
    
    return 0;
}

その出力は

0 1 2 
3 4 5 

このプログラムでは、これら 2 つの関数宣言

void f( size_t, size_t, int [][*] );

そして

void g( size_t, size_t, int [][*] );

不完全なオブジェクト タイプのパラメータ宣言があります。

例のように、同じ型がその定義であるような宣言を使用することはできません

void f( size_t m, size_t n, int a[][*] )
{
    // ...
}

3 番目のパラメーターをポインターに調整した後、コンパイラーはポインターの型を判別できないためです。つまり、ポインターは不完全なオブジェクト型 int ( * )[] を持つことになります .


コメントで指摘されているように、標準のテキストは C11 で修正されました。 (C11 J.2):

その方が理にかなっています。

ただし、エラーなしでコンパイルされる関数定義のパラメーターで不完全なオブジェクト型を使用する例を思いつきません。私が考えることができるのは、おそらく一部のコンパイラでは、未使用のパラメータが不完全なオブジェクト型を持つことを許可しているということだけです.

コメントで@Lundinが指摘したように、付録Jは参考情報であり、標準の規範的な部分ではありません。修正は、基準の規範部分の参照セクション 6.9.1 のテキストでも行われました。 6.9.1/7 の最終文の最終節が 「結果の型はオブジェクト型でなければならない」 から変更されました 「結果の型は完全なオブジェクト型でなければならない」 .

@Lundin はまた、関数定義では、不完全な型である調整されたパラメーター型が C11 6.7.6.3/4 (C99 6.7.5.3/4) による制約違反であることを指摘しています:

これは「制約」の下にリストされているため、少なくとも 1 つの診断を生成するにはプログラムを変換する必要があります。