Search code examples
clanguage-lawyerundefined-behaviorimplicit-conversionc99

Undefined Behaviour in C99 related to adjusted parameters


I don't understand the following undefined behaviour from C99 standard:

An adjusted parameter type in a function definition is not an object type (6.9.1)

From the Standard, parameters of a function shall be adjusted in two cases:

  • an array is adjusted to a pointer,
  • and a function is adjusted to a pointer to function.

In the second case the adjusted parameter of a function will indeed not be an object (as far as I know the standard distinguishes between object and function):

An identifier can denote an object; a function; a tag or a member of a structure, union...

Could you clarify the point and provide an example of such UB ?


Solution

  • The first quote from the C Standard is incorrect. It sounds like

    — An adjusted parameter type in a function definition is not a complete object type (6.9.1)

    That is you omitted the word complete.

    For example in a function declaration that is not at the same type its definition you may specify an incomplete object type like

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

    In this function declaration the declaration of the third parameter is not a complete object type because the size of the array elements is unknown.

    Here is a demonstrative program

    #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;
    }
    

    Its output is

    0 1 2 
    3 4 5 
    

    Here in the program these two function declarations

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

    and

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

    have a parameter declaration with an incomplete object type.

    You may not use such a declaration that at the same type is its definition like for example

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

    Because the compiler is unable to determine the pointer type after adjusting the third parameter to pointer. That is the pointer will have an incomplete object type int ( * )[].