Search code examples
ctype-conversionlanguage-lawyerundefined-behaviorfunction-call

Can I call a function taking a long parameter with an int argument?


Is this code undefined behavior?

extern long f(long x);

long g(int x)
{
    return f(x);
}

According to the C11 standard, in 6.5.2.2 §6:

If the function is defined with a type that includes a prototype, and [...] the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined.

In the example above, the function f is defined with a type that includes a prototype and the type of the argument x is int while the type of the parameter x is long. According to 6.2.7 §1:

Two types have compatible type if their types are the same.

Therefore, long and int are not compatible, so the behavior is undefined, right?

However, in 6.5.2.2 §7:

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.

If I correctly understand this paragraph, it means the argument x which is of type int is implicitly converted to long when the function is called. According to 6.3.1.3 §1:

When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

As int has a lower rank than long, every int variable can be represented by a long variable. Therefore, the argument x can be converted into a long. Therefore, this is not undefined behavior.

Which interpretation of the standard is right? Is my code undefined behavior or not?


Solution

  • You provided irrelevant quotes relative to your code snippet. According to the same section (6.5.2.2 Function calls)

    2 If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.

    The function f has a prototype that is visible in the call expression

    extern long f(long x);
    

    and this assignment

    int argument;
    long parameter;
    parameter = argument
    

    is correct.

    As for this quote

    6 If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:

    Then it means the following. The function calling expression does not see the function prototype. So the default argument promotions are performed. But somewhere else the function is defined with a function prototype and the promoted arguments are not compatible with function parameters. In this case you will have undefined behavior.

    Here is a demonstrative program with undefined behavior related to a function call. The compiler can issue an error message.

    #include <stdio.h>
    
    void f();
    
    int main(void) 
    {
        short x = 10;
        
        f( x );
        
        return 0;
    }
    
    void f( char *s )
    {
        printf( "s = %s\n", s );
    }
    

    or

    #include <stdio.h>
    #include <limits.h>
    
    void f();
    
    int main(void) 
    {
        unsigned int x = UINT_MAX;
        
        f( x );
        
        return 0;
    }
    
    void f( int x )
    {
        printf( "x = %hd\n", x );
    }
    

    For example in the last program the argument x in the call expression

    f( x );
    

    is promoted to the type unsigned int. But according to the function definition the function expects an argument of the type signed int and the passed value can not be stored in the type signed int. So the behavior is undefined. But your original example of a function call is not related to this quote.