1
#include<stdio.h>
int main() {
    long a = 9;
    printf("a = %d",a);//output is 9 but with a warning 'expecting long int'
}

Why can't long here be converted to int?

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Shivani
  • 85
  • 5
  • Is this C or C++? – DeiDei Jul 07 '17 at 10:57
  • 3
    To be honest, if I were the `long` and was told i'd be getting promoted to type `int` , i'd probably think you were taking the piss. The type can be truncated but printing with a format specifier won't cause that to happen. – George Jul 07 '17 at 11:00
  • 6
    Short answer: no. Passing a `long` to `printf()` with the `%d` format gives undefined behaviour. Part of the reason is that a `long` can potentially represent values that an `int` cannot, and `%d` tells `printf()` to ASSUME the passed value is of type `int`, rather than converting that argument to `int`. – Peter Jul 07 '17 at 11:00
  • 3
    That's more like demotion, not promotion – Spikatrix Jul 07 '17 at 11:07
  • @CoolGuy: It's undefined behaviour, nothing else. – too honest for this site Jul 07 '17 at 11:27
  • If you really need to use `"%d"` convert the value explicitly: `printf("a (after conversion) = %d\n", (int)a);` – pmg Jul 07 '17 at 11:31
  • 2
    @pmg: This will generate an implementtation defined result if the value is too large for an `int` (or raise a signal). – too honest for this site Jul 07 '17 at 11:43

3 Answers3

2

Variadic functions in general and printf family in particular, are odd special cases. They are notorious for their non-existent type safety, so if you pass the wrong type or use the wrong format string, you invoke undefined behavior and anything can happen.

In your case, most likely int and long happen to have the same representation so the program works despite the warning.

In the case of a regular function though, there is a kind of "demotion" taking place if you pass a larger integer type to a function expecting a smaller one. When this happens, you trigger a conversion from the larger type to the smaller, which is well-defined. (The result will however be compiler-specific if you mix types of different signedness.)

Compilers tend to warn against such implicit conversions, so it is better to do the conversion explicitly with a cast.

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

Because that's the way variadic functions behave in C language. printf is just a function from the standard library and has no special processing. It is declared as

int printf(const char restrict *fmt, ...);

And the standard (n1256 draft for C99) says (emphasize mine):

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

That means that on all parameters to printf, float are converted to double and integer promotions occur on integral arguments.

And in 6.3.1.1 Arithmetic operands / Boolean, characters, and integers

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.48) All other types are unchanged by the integer promotions.

So as long has a rank greater than int, it is left unchanged by an integer promotion, and the format shall be adapted to accept a long:

long a = 9;
printf("a = %ld",a);
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • In this case there is no integer promotion nor default argument promotion though. The OP is passing a large integer type. And since it doesn't match the format string, the code invokes undefined behavior. – Lundin Jul 07 '17 at 11:47
  • Yes @Lundin. As I mentioned above, the output appears with just a warning. – Shivani Jul 07 '17 at 13:44
1

Following passes a long, yet printf() expects an int due to "%d". Result: undefined behavior (UB). If int and long are the same size that UB might look like everything is OK, or it may fail. It is UB.

long a = 9;
printf("a = %d",a);  // UB

Why can't long here be converted to int?

long can be converted, yet code did not direct that like the below code.

printf("a = %d", (int) a);  // OK

Can integer promotion happen in the reverse order ... in variadic functions like printf()?

Integer promotions do not happen in the reverse order unless code explicitly down-casts or assigned to a narrower type.

There are cases where demotion will appear to be true with printf().
The below promotes sc to int as it is passed to printf(). printf() will take that int and due to "%hhd" will convert it to signed char and then print that numeric value.

signed char sc = 1;
printf("a = %hhd", cs); // prints 1

The below passes i to printf() as an int. printf() will take that int and due to "%hhd" will convert it to signed char and then print that numeric value. So in this case it looks like i was demoted.

int i = 0x101;
printf("a = %hhd", i);  // prints 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256