Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

better understanding type promotion of variadic parameters in c

When a variable argument function is called in c the integer parameters are promoted to int and floating point parameters are promoted to double

Since the prototype doesn’t specify types for optional arguments, in a call to a variadic function the default argument promotions are performed on the optional argument values. This means the objects of type char or short int (whether signed or not) are promoted to either int or unsigned int, as appropriate; and that objects of type float are promoted to type double. So, if the caller passes a char as an optional argument, it is promoted to an int, and the function can access it with va_arg (ap, int).

int type should be 4 byte on 32 bit machines and 8 byte on 64 bit machines, is that right?
So I wonder what append when I pass a long long int to a variable argument function like printf with %lld format.
And, again, I wonder what append when I pass a long double variable to printf with %Lf format (no matter if on 32 or 64 bit machines).

[Edited]

on a 32 bit machine, I tried this:

#include <stdio.h>

int main(void)
{
    printf("sizeof(int) %d\n", sizeof(int));
    printf("sizeof(long int) %d\n", sizeof(long int));
    printf("sizeof(long long int) %d\n", sizeof(long long int));
    printf("%lld\n", 1LL<<33);

    printf("sizeof(float) %d\n", sizeof(float));
    printf("sizeof(double) %d\n", sizeof(double));
    printf("sizeof(long double) %d\n", sizeof(long double));
    return 0;
}

The result is:

sizeof(int) 4
sizeof(long int) 4
sizeof(long long int) 8
8589934592
sizeof(float) 4
sizeof(double) 8
sizeof(long double) 12

this makes me think that not all parameters are promoted to int, otherwise I would get printed 0 instead of 8589934592.

Maybe only arguments smaller than int are promoted to int. And something similar could be for floating point types.

[Edited]

on a 64 bit machine I run this:

int main(void)
{
    printf("sizeof(int) %lu\n", sizeof(int));
    printf("sizeof(long) %lu\n", sizeof(long));
    printf("sizeof(long long) %lu\n", sizeof(long long));
    return 0;
}

and get

sizeof(int) 4
sizeof(long) 8
sizeof(long long) 8

if I understand well the standard, only char and short are promoted to int. I wonder what happen in smaller architecture, such like 16 bit or 8 bit MCU. I think that int size is dependent by architecture, but I wonder if sizeof(int) can be 1 on 8 bit architecture. In this case promotion of short to int could be impossible unless loosing some bits

like image 326
mastupristi Avatar asked Jun 07 '17 19:06

mastupristi


2 Answers

"int type should be 4 byte on 32 bit machines and 8 byte on 64 bit machines, is that right?" No. According to the Standard, ints must be at least 16 bits in width (§5.2.4.2.1), but there is no further stipulation.

When you pass a long long int to printf() it is not subject to the integer promotions (§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 (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.

If you pass a long double to printf() no conversion is made (§6.5.2.2 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.

The conversion specifiers corresponding to the arguments in the printf() statement have no bearing on these promotions and conversions, except insofar as there will be undefined behavior if the specifiers and the types of their corresponding arguments do not match.

So, the integer promotions are performed, and floats are converted to doubles, but "No other conversions are performed implicitly" (§6.5.2.2 8).

Addressing your edit to the question: "this makes me think that not all parameters are promoted to int." Correct. Only integer types with integer conversion rank "less than or equal to the rank of int and unsigned int" are subject to integer promotion. It is simpler for floating point types; floats are promoted to double. That is all.

It may be worth pointing out that, according to §6.2.5 10, there are three real floating point types, float, double, and long double. The values which may be held by a float are a subset of the values which may be held by a double, which are in turn a subset of the values which may be held by a long double. Hence, there is no possibility of promotion for long double types.

Further, according to §6.3.1.1 1:

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.

So there is no possibility of a long long int or long int ever being promoted to int or unsigned int.

As for your final concern that promotion of a short to an int may, in some implementation, be impossible without losing some bits, note that §6.2.5 8 guarantees that an int must be able to contain a short, since the conversion rank of an int must be greater than that of a short:

For any two integer types with the same signedness and different integer conversion rank (see 6.3.1.1), the range of values of the type with smaller integer conversion rank is a subrange of the values of the other type.

like image 105
ad absurdum Avatar answered Nov 15 '22 22:11

ad absurdum


int type should be 4 byte on 32 bit machines and 8 byte on 64 bit machines, is that right?

Probably not. First of all, C makes no guarantees about the size of int, apart from stating that it must be at least 2 bytes (capable of at least holding the value 2^16/2 - 1 = 32767). Nothing in C prevents int from being 8 bytes, but that's not very convenient.

The convention is: all 8- and 16 bit computers use 16 bit int. All other computers use 32 bit int. This is industry de facto standard. Any deviations from this convention would be very exotic, although in theory allowed by C.


So I wonder what append when I pass a long long int to a variable argument function like printf with %lld format.

You get a long long int. No promotion takes place, because the variable is not one of the mentioned small types. The format specifier used is irrelevant to the promotion rules.


this makes me think that not all parameters are promoted to int

Indeed. Only the types that the standard mentions are promoted. The formal definition of the default argument promotions can be found in C11 6.5.2.2/6:

...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.

Meaning that only the small integer types (see the integer promotion rule) and float gets promoted. No other types.


Why you think that your examples printing various type sizes have any relevance in relation to the default argument promotions, I have no idea.

like image 36
Lundin Avatar answered Nov 15 '22 23:11

Lundin