Is Char Default-Promoted

Is char default-promoted?

C++

In C++ 2011 (ISO/IEC 14882:2011), the relevant parts seem to be:

§5.2.2 Function Call [expr.call]


¶6 A function can be declared to accept fewer arguments (by declaring default arguments (8.3.6)) or more
arguments (by using the ellipsis, ..., or a function parameter pack (8.3.5)) than the number of parameters
in the function definition (8.4). [Note: this implies that, except where the ellipsis (...) or a function
parameter pack is used, a parameter is available for each argument. —end note]

¶7 When there is no parameter for a given argument, the argument is passed in such a way that the receiving
function can obtain the value of the argument by invoking va_arg (18.10). [Note: This paragraph does not
apply to arguments passed to a function parameter pack. Function parameter packs are expanded during
template instantiation (14.5.3), thus each such argument has a corresponding parameter when a function
template specialization is actually called. —end note] The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and
function-to-pointer (4.3) standard conversions are performed on the argument expression. An argument that
has (possibly cv-qualified) type std::nullptr_t is converted to type void* (4.10). After these conversions,
if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the
program is ill-formed. Passing a potentially-evaluated argument of class type (Clause 9) having a nontrivial
copy constructor, a non-trivial move contructor, or a non-trivial destructor, with no corresponding
parameter, is conditionally-supported with implementation-defined semantics.

If the argument has integral
or enumeration type that is subject to the integral promotions (4.5), or a floating point type that is subject
to the floating point promotion (4.6), the value of the argument is converted to the promoted type before
the call. These promotions are referred to as the default argument promotions.

I've separated the last two sentences to give them emphasis. They're a continuous part of paragraph 7 in the standard.

§4.5 Integral promotions [conv.prom]


¶1 A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion
rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all
the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned
int
.

¶2 A prvalue of type char16_t, char32_t, or wchar_t (3.9.1) can be converted to a prvalue of the first of
the following types that can represent all the values of its underlying type: int, unsigned int, long int,
unsigned long int, long long int, or unsigned long long int. If none of the types in that list can
represent all the values of its underlying type, a prvalue of type char16_t, char32_t, or wchar_t can be
converted to a prvalue of its underlying type.

Etc.


C

C has two contexts where arguments are default promoted. One is when there is no prototype in scope for a function (first covered by another answer), and the second when there is a prototype with ellipsis. C++ does not allow the first case at all, of course. These quotes are from the same sections of the standard as chosen by another answer, but the snippets here are somewhat longer. They were found by independent analysis of the standard, and it was only when cross-checking that I noticed that the sections are the same.

In C 2011 (ISO/IEC 9899:2011), the relevant parts seem to be:

§6.5.2.2 Function calls


¶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:

— one promoted type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in both types;

— both types are pointers to qualified or unqualified versions of a character type or
void.

¶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. The ellipsis notation in a function prototype declarator causes
argument type conversion to stop after the last declared parameter. The default argument
promotions are performed on trailing arguments.

The 'integer promotions' are defined in §6.3.1.1:

§6.3.1 Arithmetic operands


§6.3.1.1 Boolean, characters, and integers


¶1 Every integer type has an integer conversion rank defined as follows:

— No two signed integer types shall have the same rank, even if they hav e the same
representation.

— The rank of a signed integer type shall be greater than the rank of any signed integer
type with less precision.

— The rank of long long int shall be greater than the rank of long int, which
shall be greater than the rank of int, which shall be greater than the rank of short
int, which shall be greater than the rank of signed char.

— The rank of any unsigned integer type shall equal the rank of the corresponding
signed integer type, if any.

— The rank of any standard integer type shall be greater than the rank of any extended
integer type with the same width.

— The rank of char shall equal the rank of signed char and unsigned char.

— The rank of _Bool shall be less than the rank of all other standard integer types.

— The rank of any enumerated type shall equal the rank of the compatible integer type
(see 6.7.2.2).

— The rank of any extended signed integer type relative to another extended signed
integer type with the same precision is implementation-defined, but still subject to the
other rules for determining the integer conversion rank.

— For all integer types T1, T2, and T3, if T1 has greater rank than T2 and T2 has
greater rank than T3, then T1 has greater rank than T3.

¶2 The following may be used in an expression wherever an int or unsigned int may
be used:

— An object or expression with an integer type (other than int or unsigned int)
whose integer conversion rank is less than or equal to the rank of int and
unsigned int.

— A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type (as restricted by the width, for a
bit-field), the value is converted to an int; otherwise, it is converted to an unsigned
int
. These are called the integer promotions.58) All other types are unchanged by the
integer promotions.

58) The integer promotions are applied only: as part of the usual arithmetic conversions, to certain
argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the
shift operators, as specified by their respective subclauses.


I note that at one time, the question listed the function void f(...);, which is a C++ function and not a C function; C does not allow the ellipsis to appear as the only argument to the function. The question has since been updated to void f(int, ...); which is valid in both C and C++.

Are chars automatically promoted in C expressions?

chars are automatically promoted to integers in C expressions

Yes, they are. C99 section 6.3.1.8, Usual arithmetic conversions:

Many operators that expect operands of arithmetic type cause conversions and yield result
types in a similar way. The purpose is to determine a common real type for the operands
and result. For the specified operands, each operand is converted, without change of type
domain, to a type whose corresponding real type is the common real type. Unless
explicitly stated otherwise, the common real type is also the corresponding real type of
the result, whose type domain is the type domain of the operands if they are the same,
and complex otherwise. This pattern is called the usual arithmetic conversions:

  • First, if the corresponding real type of either operand is long double, the other
    operand is converted, without change of type domain, to a type whose corresponding real type is long double.
  • Otherwise, if the corresponding real type of either operand is double, the other
    operand is converted, without change of type domain, to a type whose
    corresponding real type is double.
  • Otherwise, if the corresponding real type of either operand is float, the other
    operand is converted, without change of type domain, to a type whose
    corresponding real type is float.62)
  • Otherwise, the integer promotions are performed on both operands. Then the
    following rules are applied to the promoted operands:

    • If both operands have the same type, then no further conversion is needed.
    • Otherwise, if both operands have signed integer types or both have unsigned
      integer types, the operand with the type of lesser integer conversion rank is
      converted to the type of the operand with greater rank.
    • Otherwise, if the operand that has unsigned integer type has rank greater or
      equal to the rank of the type of the other operand, then the operand with
      signed integer type is converted to the type of the operand with unsigned
      integer type.
    • Otherwise, if the type of the operand with signed integer type can represent
      all of the values of the type of the operand with unsigned integer type, then
      the operand with unsigned integer type is converted to the type of the
      operand with signed integer type.
    • Otherwise, both operands are converted to the unsigned integer type
      corresponding to the type of the operand with signed integer type.

Integer promotions are described on Section 6.3.1.1.2:

The following may be used in an expression wherever an int or unsigned
int may be used:

  • An object or expression with an integer type whose integer conversion rank is less than or equal to the rank of int and unsigned
    int.
  • A bit-field of type _Bool, int, signed int, or unsigned int

If an int can represent all values of the original type, the value is
converted to an int; otherwise, it is converted to an unsigned int.
These are called the integer promotions. All other types are unchanges
by the integer promotions.

The rank of a char is less than or equal to that of an int, so char is included in here.

(As a footnote, it is mentioned that integer promotions are only applied as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, - and ~, and to both operands of the shift operators).

As mentioned in the comments, integer promotion is also performed on function-call arguments.

Char' is promoted to 'int' when passed through in C

The compiler just told you why:

'char' is promoted to 'int' when passed through '...'

According to the C language specification, usual arithmetic conversions are applied to the unnamed arguments of variadic functions. Hence, any integer type shorter than int (e. g. bool, char and short) are implicitly converted int; float is promoted to double, etc.

Do what the compiler asks you for (use va_arg() with int as its second parameter), else your code will invoke undefined behavior.

Default argument promotions in C function calls

Upvoted AProgrammer's answer—those are the real goods.

For those of you who are wondering why things are this way: in the dark ages before 1988, there was no such thing as a function prototype in classic "K&R" C, and the default argument promotions were instituted because (a) there were essentially "free", as it costs no more to put a byte in a register than to put a word in a register, and (b) to cut down on potential errors in parameter passing. That second reason never quite cut it, which was why the introduction of function prototypes in ANSI C was the single most important change ever in the C language.

As to when default promotions kick in: default argument promotions are used exactly when the expected type of the argument is unknown, which is to say when there's no prototype or when the argument is variadic.

I want default argument promotion's example

For historical reasons, functions in C can be declared the old way, without a prototype, as:

type function()

or the new way, with a prototype, as:

type function(type parameter)
type function(type parameter, type parameter)
type function(type parameter, type parameter, type parameter)
… and so on

Thus, a function declaration in which the types of the parameters are given is said to have a prototype. This affects preparation of the arguments when calling a function. With a prototype, the arguments are converted to the declared types of the parameters. Without a prototype, the arguments are converted to default types, using rules called the default argument promotions. (Also, if a function is just being declared, not defined, the names of the parameters can be omitted, leaving a prototype that just lists types, as in type function(type, type, type).)

Because of the old grammar, to declare a function with a prototype that says it has no parameters, you need to explicitly say void: type function(void). (This is different in C++, which does not have to support the old grammar. In C++, type function() is a prototype with no parameters.)

Additionally, a prototype can specify that there are variable arguments by putting ,... after one or more regular parameters:

type function(type parameter,...)

In this case, the first arguments are converted to the parameter types, but the arguments that correspond to the ... are converted using the default argument promotions. printf is declared this way.

The default argument promotions are:

  • Integers narrower (technically, of lesser rank) than int are promoted to int if it can represent all the values of the source type or unsigned int otherwise.
  • float arguments are converted to double.

There is also some finickiness about bit-fields in the default argument promotions, which I cannot say has ever arisen in code for me.

History

In the old C grammar, a function would be defined with:

type function(name0, name1, name2)
type name0;
type name1;
type name2;

and it would be declared without a prototype, as with type function(). This means the caller did not know the actual types of the parameters. But you could not just pass a char value for a char argument or a short value for a short argument, because C, in trying to be flexible so it could work on many types of computers, had rules about char and short values being promoted to int in expressions. Additionally, character constants like 'X' have type int, not char, so, if somebody called a function with foo('X'), the compiler would not know if foo really wanted just a char rather than an int. So the default argument promotions were made to match the integer promotions.

Later versions of C fixed this by providing a way to declare the argument types in declarations visible to the caller, so the arguments always match the parameters (and the compiler has more information it can use to provide error messages). But the old grammar still has to be supported so that old code can be compiled.

More

The phrase in the C standard, “the expression that denotes the called function,” is used because you can call functions through pointers, not just their names. For example, we can write:

    int (*FunctionPointer)() = (int (*)()) printf;

and then call printf using FunctionPointer("Hello, world.\n");. Then “the expression that denotes the called function” is FunctionPointer, and it does not have a prototype even though printf does. (There is no good reason to do this with the printf function, but some esoteric code may do some unsavory things.)

You can initially declare a function without a prototype:

int foo();

and later add a prototype:

int foo(float x, char *y);

The compiler will merge the declarations, and the resulting foo will have a prototype.

Default argument and parameter promotions in C

Before C was standardized (aka before C89), functions were defined differently. The style is still supported in C11 for backwards-compatibility. Don't use it unless the whole purpose is to have fun:

int add_ints(); //forward-declaration has no parameters

add_ints(a, b)
//implicit type for return and parameters is int, this only works in pre-standard C or C89/C90
//int a, b; //remove this comment in C99/C11 for it to compile (also add return type int)
{
return a + b; //side note: old K&R compilers required parantheses around the return expression
}

In a way, these functions have parameters that behave like varargs. The caller doesn't know what parameters the function expects (same as with varargs). It is able to pass it any parameters and any number of them. However, it is of course undefined behavior if the number of parameters in the call statement doesn't match the number of parameters in the declaration.

Of course, there is a problem that arises from this. If the caller wants to pass a short, how will it know whether the function is expecting a short (and pass it directly) or an int (and needs to convert it)? It cannot, so a common ground was reached. It has been decided that:

  • char and short get promoted to int
  • float gets promoted to double

This happens for all functions defined this way (K&R style) and for varargs parameters. This way, a K&R function will never expect a short parameter, thus the compiler will always promote short parameters to int.

Of course, as @aschepler said, you can still define the function like:

short add_shorts(a, b)
short a, b;
{
return a + b;
}

This means that the parameters are first converted to int and passed to the function and only then does the function convert them to short and add them.

Be careful with functions like printf():

printf("%.f", 3); //passes an int: UB and also wrong answer (my compiler prints 0)
printf("%.f", 3.0); //correct
printf("%.f", (double)3); //correct

You may actually see K&R functions quite often, especially if the author didn't pay attention to add the void keyword to a function that takes no parameters:

int f1() //K&R function
{
return 0;
}
int f2(void) //Standard function
{
return 0;
}

int main(void) //Don't forget void here as well :P
{
int a = f1(); //Returns 0
int b = f2(); //Returns 0
int c = f1(100); //UB - invalid number of parameters, in practice just returns 0 :)
int d = f2(100); //Compiler error - parameter number/types don't match

//A good compiler would give a warning for call #3, but mine doesn't :(
}

EDIT: Not sure why, but cppreference classifies functions defined like f1() as their own type of function (parameter-less without void), instead of K&R functions. I don't have the standard in front of me, but even if the standard says the same thing, they should behave the same and they have the history I mentioned.

Default argument promotions

Function declarations in C

Is unsigned char always promoted to int?

are implementations allowed to promote them to unsigned int?

Implementations will promote to unsigned int if not all unsigned char values are representable in an int (as ruled by 6.2.5p9 in C99). See below for implementation examples.

If so, does that imply that an implementation could theoretically have an unsigned char value which is not in the subrange of an int?

Yes, example: DSP cpu with CHAR_BIT 16 or 32.

For example, TI C compiler for TMS320C55x: CHAR_BIT is 16 and UCHAR_MAX 65535, UINT_MAX 65535 but INT_MAX 32767.

http://focus.ti.com/lit/ug/spru281f/spru281f.pdf

default argument promotion of pointer

Nothing. Default argument promotions don't apply to arguments of pointer type so they are left unchanged in type and value.

When will default argument promotions happen?

Default promotions will happen before the function is called, in the calling context.

If you're really asking about the circumstances under which default promotions are carried out, that's covered in the excerpt, though it's such a tiny piece that it's easy to miss: "When there is no parameter for a given argument...". In other words, it's essentially identical to the situation in C, with the exception that a C-style function declaration that doesn't specify parameter types simply doesn't exist in C++. Therefore, the only time you can have an argument without a parameter specifying its type is when a function has an explicit ellipsis, such as printf: int printf(char const *format, ...);.

Will char and short be promoted to int before being demoted in assignment expressions?

The answer is Neither 1 nor 2.

The value of ch2 is directly assigned to ch1. With the assignment operator, the left-hand operand is the target.

There are no promotions; the behaviour is specified by C11 6.5.16.1/2:

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

In the previous paragraph it is defined:

The type of an assignment expression is the type the left operand would have
after lvalue conversion.

where "lvalue conversion" means lvalue-to-rvalue conversion, which has the effect of removing const, volatile and _Atomic qualifiers for the type. (It also has an effect on array types, but that is moot here as arrays cannot appear as the left operand of an assignment).

Your quote from "C Primer Plus" is a mistake by the book. Integer promotions do not occur in all expressions. In fact, they occur when the integer is an operand of an operator, and the definition of that operator specifies that its operands undergo the integer promotions.

Most operators do specify that, but the assignment operator, and sizeof for example, do not. You can check the C standard's specification of each operator to see whether that operator promotes its operands.



Related Topics



Leave a reply



Submit