How does %d format specifier work? - c

#include<stdio.h>
main()
{
char x ;
x = 'a' ;
printf("%d",x);
}
In this %d format specifier should read 4 bytes being an integer format specifier but x being an character is stored in 1 byte only. so the remaining 3 bytes should have garbage value and the output should be random.
But the output is always coming 97 which is the ASCII value of 'a'.
Can anyone tell how the format specifier works ?

Function arguments that don't match a declared parameter type of the function (i.e. because the function allows for variable arguments, like printf, or because no function prototype has been provided) undergo the default argument promotions (cf. C11 6.5.2.2), which turn a char value into a value of int or unsigned int. Therefore, passing a to printf matches the requirement for %d.

Related

How printf() function knows the type of its arguments

Consider the following program,
#include <stdio.h>
int main()
{
char a = 130;
unsigned char b = 130;
printf("a = %d\nb = %d\n",a,b);
return 0;
}
This program will show the following output.
a = -126
b = 130
My question is how printf() function comes to know the type of a is signed and type of b is unsigned to show result like above?
printf() doesn't know the types, that's why you have to give a correct format string. The prototype for printf() looks like this:
int printf(const char * restrict format, ...);
So, the only argument with a known type is the first one, the format string.
This also means that any argument passed after that is subject to default argument promotion -- strongly simplified, read it as any integer will be converted to at least int -- or ask google about the term to learn each and every detail ;)
In your example, you have implementation defined behavior:
char a = 130;
If your char could represent 130, that's what you would see in the output of printf(). Promoting the value to int doesn't change the value. You're getting a negative number instead, which means 130 overflowed your char. The result of overflowing a signed integer type during conversion in C is implementation defined, the value you're getting probably means that on you machine, char has 8 bits (so the maximum value is 127) and the signed integer overflow resulted in a wraparound to the negative value range. You can't rely on that behavior!
In short, the negative number is created in this line -- 130 is of type int, assigning it to char converts it and this conversion overflows.
Once your char has the value -126, passing it to printf() just converts it to int, not changing the value.
The additional arguments to printf() are formatted according to the type specifier. See here for a list of C format specifiers.
https://fr.cppreference.com/w/c/io/fprintf
It's true that one would not expect b to be printed as 130 in your example since you used the %d specifier and not %u. This surprising behavior seems to be explained here.
Format specifier for unsigned char
I hope I got your question well.
Edit: I can not comment Felix Palmen's answer on account on my low reputation. default argument promotion indeed seems to be the key here, but to me the real question here besides the overflow of a is why b is still printed as 130 despite the use of the signed specifier. It can also be explained with default argument promotion but that should be made more precise.
You need to have a look at the definition of printf statement in stdio.h. You already got the answer in comment printf just write the string pointed by format to stdout.
It's variadic function and it use vargas to get all the arguments in variable-length argument list.
You
This is from the glibc from the GNU version.
int __printf (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
What vfprintf does?
It just writes the string pointed by format to the stream, replacing any format specifier in the same way as printf does, but using the elements in the variable argument list identified by arg instead of additional function arguments.
More information about the vfprintf
printf() does not know the data type of arguments. It works on format specifier you passed. The data type you are using is char (having range from -128 to +127) and unsigned char (having range from 0 to 255). Your output for a is overflowed after 127. So the output comes to -126.

Trying to understand printf a pointer with a char specifier (%c)

#include<stdio.h>
void main()
{
int n = 2;
printf("%c",&n);
}
Output: L
On using %d it of course gives the address of variable n but why is it outputting L on using %c?
It is undefined behaviour because you have used wrong format specifier.
C11 Standard: 7.21.6.1 : paragraph 9:
If a conversion specification is invalid, the behavior is
undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
It means anything can happen if printf is called with a wrong format specifier.
As the other answer of rsp said the behaviour is undefined and anything could happen.
In your case the following happens:
It reads one byte of &n because a character is of one byte size which is mostly 8 bits long, see CHAR_BIT explanation on SO. Then it prints it as character.
This byte could be the first byte of &n if the types int* and int are the same at your machine. That would mean that your variable n lies at address 0x48... (big-endian) or 0x...48 (little-endian) as 0x48 is H in the Ascci code. I assume that your program is using Ascii for encoding which doesn't necessarily have to.
(You changed the character from H to L but I leave this answer as it is.)
Also this byte can lie somewhere in the middle of the &n if an int* exceeds the size of an int at your system.
You should compile with more warnings enabled for example -Wall in gcc and you will get:
warning: format ‘%c’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
In the case you cast that value to a char the behaviour will be well defined but rely on the implementation as a cast of a pointer type to an integer is implementation defined.
Nevertheless what you're doing makes no sense also with a cast it will not do.
It's because you told printf() function to show the first 1-byte of int n's address as a char.

How integers numbers are printed with %f specifier? [duplicate]

This question already has answers here:
Unexpected output of printf
(4 answers)
Closed 9 years ago.
#include<stdio.h>
main()
{
int a = 66;
printf("%f", a);
}
It is printing 0.0000 as an answer, why?
And %f is replaced with %c it is printing B. I am not getting the reason.
Please explain !!
It is because your program invokes undefined behavior. You may get anything, either expected or unexpected.
C11: 7.29.2 Formatted wide character input/output functions:
If a conversion specification is invalid, the behavior is undefined.335) If any argument is not the correct type for the corresponding conversion specification, the behavior is
undefined.
printf("%f",a);
^ %f specifier expects arguments of type float/ double.
And %f is replaced with %c it is printing B. I am not getting the reason
ASCII code of character B is 66. Using %c specifier prints the (printable) character itself.
For printf():
[...] If any argument is not the type expected by the corresponding conversion specifier, or if there are less arguments than required by format, the behavior is undefined. If there are more arguments than required by format, the extraneous arguments are evaluated and ignored [...]
Although, for %c, you correctly get the expected 'B' (whose ASCII code is 66), it is not recommended (imaging when you pass in 666, which will get unexpected result as no ASCII code is 666). At least, you should do type-casting beforehand:
printf("%f", (float)a);
printf("%c", (char)a);
Again, remember always use the corresponding specifiers for different types.
If you use %f specifier and do not provide a float number, but int, the behaviour is undefined. In practice the memory starting in the location where your int is will be interpreted as float number, so what gets printed depends on internal representation of ints and floats.
For %c you got B since %c interprets your int as character code, and B's code is 66.
%f is used to print floating point number
%c is used to print character
%d is used to print integer number
so when you are using %c printf function converting from ascii value to character of B.
if you want to print the integer
printf("%d",a);
%c prints the character corresponding to 66 into ascii table.
By default %f will print 5 digits after the decimal point, but you can change the behaviour with %.3f (for 3 digits)

why i am not getting the expected output?

int main()
{
int x;
float y;
char c;
x = -4443;
y = 24.25;
c = 'M';
printf("\nThe value of integer variable x is %f", (float)x);
printf("\nThe value of float variable y is %d", y);
printf("\nThe value of character variable c is %f\n",c);
return 0;
}
Output:
The value of integer variable x is -4443.000000
The value of float variable y is 0
The value of character variable c is 24.250000
Why am I not getting the expected output?
But when I am using external casting I am getting expected output which is:
The value of integer variable x is -4443.000000
The value of float variable y is 24
The value of character variable c is 77.000000
why i am not getting the expected output ?
Short answer: Because your expectations are wrong.
You're instructing the compiler to read an integer from where y is. Which is wrong. Format specifier don't tell the compiler to do casts, just what type to expect, and trust you to provide the right type.
The behaviour can be due to the fact that, for example, a float is stored in 8 bytes. The high-order bytes will be 0 in this case. But an int is stored in 4 bytes. So you tell the compiler read the int from where y is, it reads the first 4 bytes, which are 0, and prints 0...
EDIT: As John pointed out in the comments, this is UB, which means that anything can happen:
7.21.6.1/9
If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
Many computing platforms pass different types of arguments in different ways. On some platforms, floating-point arguments are passed in special floating-point registers. On most platforms, integer arguments are passed in general processor registers. Large arguments, such as structures, are stored somewhere in memory, and a pointer is passed instead (invisibly to the C source code). Once the few registers available for arguments are used, the remaining arguments are typically passed on the stack.
When you call printf, the compiler does not match the arguments you pass to the conversion specifiers in the format string. (Except that a good compiler will check and issue a warning if the types do not match.) In order to operate, the printf routine reads the format string and, when it finds a conversion specifier, it reads data from where the corresponding argument should be. If you specify “%d” but pass a float, the printf routine may read data from a general processor register, but the float value is in a floating-point register. Therefore, the value that is printed will be whatever data happened to be in the general processor register.
Similarly, when you specify “%f” but pass an integer, the printf routine may read from a floating-point register, but the integer value is in a general processor register.
The compiler will not convert printf arguments to the target type and might not warn you about the mismatches. You must match the conversion specifiers in the format string to the argument types.
Bonus: Here are documents describing how arguments are passed to subroutines on one platform (Mac OS X).
You cannot format a char as a float "%f", use "%c" or "%d" instead. I find that http://www.cplusplus.com/reference/clibrary/cstdio/printf/ is a good reference.
The format specifiers and the types of the arguments don't match, which I believe causes undefined behavior. printf doesn't do casting for you, so you have to explicitly cast the arguments.

printf too smart casting from char to int?

Why does the following call:
printf("%d %d", 'a', 'b');
result in the "correct" 97 98 values?
%d indicates the function has to read 4 bytes of data, and printf shouldn't be able to tell the type of the received arguments (besides the format string), so why isn't the printed number |a||b||junk||junk|?
Thanks in advance.
In this case, the parameters received by printf will be of type int.
First of all, anything you pass to printf (except the first parameter) undergoes "default promotions", which means (among other things) that char and short are both promoted to int before being passed. So, even if what you were passing really did have type char, by the time it got to printf it would have type int. In your case, you're using a character literal, which already has type int anyway.
The same is true with scanf, and other functions that take variadic parameters.
Second, even without default promotions, character literals in C already have type int anyway (§6.4.4.4/10):
An integer character constant has type int.
So, in this case the values start with type int, and aren't promoted--but even if you started with chars, something like:
char a = 'a';
printf("%d", a);
...what printf receives would be of type int, not type char anyway.
In C, a char literal is a value of type int.
it prints the DEC ASCII for the characters entered by you.

Resources