How printf() function knows the type of its arguments - c

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.

Related

Do format specifiers perform implicit type conversion?

#include <stdio.h>
int main(void) {
int x = 5;
int y = &x;
printf("Signed Value of Y: %d \n", y);
printf("Unsigned Value of Y: %u", y);
return 0;
}
Since y is of int type, using %d gives a possibly-signed output, whereas %u gives an unsigned output. But y is of int type, so why does %u give unsigned output? Is there an implicit type conversion?
"Re: But y is of int type, So why does %u give unsigned output?"
From C11:
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.
where,
undefined — The behavior for something incorrect, on which the
standard does not impose any requirements. Anything is allowed to
happen, from nothing, to a warning message to program termination, to
CPU meltdown, to launching nuclear missiles (assuming you have the
correct hardware option installed).
— Expert C Programming.
Effectively, a printf call is two separate things:
All the arguments are prepared to send to the function.
The function interprets the format string and its other arguments.
In any function call, the arguments are prepared according to rules involving the argument types and the function declaration. They do not depend on the values of the arguments, including the contents of any string passed as an argument, and this is true of printf too.
In a function call, the rules are largely (omitting some details):
If the argument corresponds to a declared parameter type, it is converted to that type.
Otherwise (if the argument corresponds to the ... part of a function declaration or the called function is declared without specifying parameter types), some default promotions are applied. For integers, these are the integer promotions, which largely (omitting some details) convert types narrower than int to int. For floating-point, float is promoted to double.
printf is declared as int printf(const char * restrict format, ...);, so all its arguments other than the format string correspond to ....
Inside printf, the function examines its format string and attempts to perform the directives given in the format string. For example, if a directive is %g, printf expects a double argument and takes bits from the place it expects a double argument to be passed. Then it interprets those bits as a double, constructs a string according to the directive, and writes the string to standard output.
For a %d or %u directive, printf expects an int or an unsigned int argument, respectively. In either case, it takes bits from the place it expects an int or an unsigned int argument to be passed. In all C implementations I am aware of, an int and an unsigned int argument are passed in the same place. So, if you pass an int argument but use %u, printf will get the bits of an int but will treat them as if they were the bits of an unsigned int. No actual conversion has been performed; printf is merely interpreting the bits differently.
The C standard does not define the behavior when you do this, and a C implementation would be conforming to the standard if it crashed when you did this or if it processed the bits differently. You should avoid it.
Is there an implicit type conversion?
Sort of. A function such as printf that accepts a variable number of arguments does not automatically know the number of variable arguments it actually receives on any call, or their types. Conversion specifications such as %d and %u collectively tell printf() how many variable arguments to expect, and individually they tell printf what type to expect for each argument. printf() will try to interpret the argument list according to these specifications.
The C language specification explicitly declines to say what happens when the types of printf arguments do not correspond properly to the conversion specifications in the accompanying format string. In practice, however, some pairs of data types have representations similar enough to each other that printf()'s attempt to interpret data of one type as if it were the other type is likely (but not guaranteed) to give the appearance of an implicit conversion from one type to the other. Corresponding signed and unsigned integer types are typically such pairs.
You should not rely on such apparent conversions actually happening. Instead, properly match argument types with conversion specifications. Correct mismatches by choosing a different conversion specification or performing an appropriate explicit type conversion (a typecast) on the argument.

I get previous float value when I am printing new value

I am getting output 0.23 from second printf. But typecasting gives required output. If I am not using type casting previous value is printed.
Compiler version is GCC 6.3
#include <stdio.h>
int main() {
printf("%f ", 0.23);
printf("%f", 0);
return 0;
}
LINK FOR IDE
in
> printf("%f",0);
You ask to print a double but you give an int, this is contradictory
You are not in the case where the generated code makes a double from the int because printf is not int printf(const char *, double); but int printf ( const char * format, ... ); and the compiler does not look at the format to make the necessary conversions ( but in a lot of cases the compiler warn you )
When prints access to the second argument is does to get a double using 64b and probably your int use only 32b, the behavior is undefined.
(edit, thank you #chqrlie)
I get previous float value when i am printing new value
In your case may be printf retrieves a double value from the MMX registers as opposed to the int value that was passed via the stack or regular registers... which may explain why the same value gets printed twice. But of course as always with undefined behavior, anything else could happen at any time
The problem is a combination of two factors:
The first is that for vararg functions like printf, the compiler will not do any implicit conversions of the arguments. So the 0 in the argument list is an integer constant (of type int).
The second factor is the mismatching format specifier. The printf function doesn't know anything about the arguments being passed, except what is specified in the format string. Mismatching format and argument type leads to undefined behavior. And since the "%f" specifier make printf expect a value of type double, and you have given an int value, you have such a mismatch.

why printf behaves differently when we try to print float as a hexadecimal? [duplicate]

I tried to print character as a float in printf and got output 0. What is the reason for this.
Also:
char c='z';
printf("%f %X",c,c);
is giving some weird output for hexadecimal while output is correct when I do this:
printf("%X",c);
why is it so?
The printf() function is a variadic function, which means that you can pass a variable number of arguments of unspecified types to it. This also means that the compiler doesn't know what type of arguments the function expects, and so it cannot convert the arguments to the correct types. (Modern compilers can warn you if you get the arguments wrong to printf, if you invoke it with enough warning flags.)
For historical reasons, you can not pass an integer argument of smaller rank than int, or a floating type of smaller rank than double to a variadic function. A float will be converted to double and a char will be converted to int (or unsigned int on bizarre implementations) through a process called the default argument promotions.
When printf parses its parameters (arguments are passed to a function, parameters are what the function receives), it retrieves them using whatever method is appropriate for the type specified by the format string. The "%f" specifier expects a double. The "%X" specifier expects an unsigned int.
If you pass an int and printf tries to retrieve a double, you invoke undefined behaviour.
If you pass an int and printf tries to retrieve an unsigned int, you invoke undefined behaviour.
Undefined behaviour may include (but is not limited to) printing strange values, crashing your program or (the most insidious of them all) doing exactly what you expect.
Source: n1570 (The final public draft of the current C standard)
You need to use a cast operator like this:
char c = 'z';
printf("%f %X", (float)c, c);
or
printf("%f %X", (double)c, c);
In Xcode, if I do not do this, I get the warning:
Format specifies specifies 'double' but the argument has type 'char', and the output is 0.000000.
I tried to print character as a float in printf and got output 0. What is the reason for this.
The question is, what value did you expect to see? Why would you expect something other than 0?
The short answer to your question is that the behavior of printf is undefined if the type of the argument doesn't match the conversion specifier. The %f conversion specifier expects its corresponding argument to have type double; if it isn't, all bets are off, and the exact output will vary.
To understand the floating point issue, consider reading: http://en.wikipedia.org/wiki/IEEE_floating_point
As for hexadecimal, let me guess.. the output was something like... 99?
This is because of encodings.. the machine has to represent information in some format, and usually that format entails either giving meanings to certain bits in a number, or having a table of symbols to numbers, or both
Floating points are sometimes represented as a (sign,mantissa,exponent) triplet all packed in a 32 or 64 bit number - characters are sometimes represented in a format named ASCII, which establishes which number corresponds to each character you type
Because printf, like any function that work with varargs, eg: int foobar(const char fmt, ...) {} tries to interpret its parameter to certain type.
If you say "%f", then pass c (as a char), then printf will try to read a float.
You can read more here: var_arg (even if this is C++, it still applies).

why printf behaves differently when we try to print character as a float and as a hexadecimal?

I tried to print character as a float in printf and got output 0. What is the reason for this.
Also:
char c='z';
printf("%f %X",c,c);
is giving some weird output for hexadecimal while output is correct when I do this:
printf("%X",c);
why is it so?
The printf() function is a variadic function, which means that you can pass a variable number of arguments of unspecified types to it. This also means that the compiler doesn't know what type of arguments the function expects, and so it cannot convert the arguments to the correct types. (Modern compilers can warn you if you get the arguments wrong to printf, if you invoke it with enough warning flags.)
For historical reasons, you can not pass an integer argument of smaller rank than int, or a floating type of smaller rank than double to a variadic function. A float will be converted to double and a char will be converted to int (or unsigned int on bizarre implementations) through a process called the default argument promotions.
When printf parses its parameters (arguments are passed to a function, parameters are what the function receives), it retrieves them using whatever method is appropriate for the type specified by the format string. The "%f" specifier expects a double. The "%X" specifier expects an unsigned int.
If you pass an int and printf tries to retrieve a double, you invoke undefined behaviour.
If you pass an int and printf tries to retrieve an unsigned int, you invoke undefined behaviour.
Undefined behaviour may include (but is not limited to) printing strange values, crashing your program or (the most insidious of them all) doing exactly what you expect.
Source: n1570 (The final public draft of the current C standard)
You need to use a cast operator like this:
char c = 'z';
printf("%f %X", (float)c, c);
or
printf("%f %X", (double)c, c);
In Xcode, if I do not do this, I get the warning:
Format specifies specifies 'double' but the argument has type 'char', and the output is 0.000000.
I tried to print character as a float in printf and got output 0. What is the reason for this.
The question is, what value did you expect to see? Why would you expect something other than 0?
The short answer to your question is that the behavior of printf is undefined if the type of the argument doesn't match the conversion specifier. The %f conversion specifier expects its corresponding argument to have type double; if it isn't, all bets are off, and the exact output will vary.
To understand the floating point issue, consider reading: http://en.wikipedia.org/wiki/IEEE_floating_point
As for hexadecimal, let me guess.. the output was something like... 99?
This is because of encodings.. the machine has to represent information in some format, and usually that format entails either giving meanings to certain bits in a number, or having a table of symbols to numbers, or both
Floating points are sometimes represented as a (sign,mantissa,exponent) triplet all packed in a 32 or 64 bit number - characters are sometimes represented in a format named ASCII, which establishes which number corresponds to each character you type
Because printf, like any function that work with varargs, eg: int foobar(const char fmt, ...) {} tries to interpret its parameter to certain type.
If you say "%f", then pass c (as a char), then printf will try to read a float.
You can read more here: var_arg (even if this is C++, it still applies).

C datatype : Between Short and Int

I read a book talking about C , it's better for me to present the code first and question in the latter.
First Code
#include <stdio.h>
int main(void)
{
short num = 3;
printf("%hd\n" , num );
return 0;
}
Second Code
#include <stdio.h>
int main(void)
{
short num = 3;
printf("%d\n" , num );
return 0;
}
Special note: I'm using intel based pc so int size is 32-bit.
Question :
1.) The book mention this two code could run correctly although one of it uses the %hd specifier while the other uses %d specifier.
2.)The reason from the book is that because C mechanism would automatically convert the type short to int for faster computation,that is why by using the %d specifier or even %ld which is 32-bit would yield the correct result too.
3.)My question is , when does this conversion occurred??Is it during the time we passed it as an argument to the printf() function , just like how float variable is converted to double when it is passed as an expression or an argument, or by the time we initialize the variable with a value 3??
4.)Actually I've done a small experiment , that is by printing out the size of the variable num using the sizeof operator along with printf() function , and it shows me 2 bytes.But i still not sure when the conversion happen.
5.)If the conversion occurred during the time we assigned the value to the short variable,what's the point of creating a short variable??(**This question should be ignore if it's not the case)
Your help is much appreciated
Yes, %d and %hd are equivalent in this case. printf() is a variadic function, so the rules say that "integer promotions" are applied to the arguments. printf() doesn't see a short value at all, it just sees an int.
%ld is for long int. This could be bigger in size than a plain int, so here the book is wrong.
The conversion occurs in the call to printf(). Any short int passed to printf() is converted to int by the compiler. The short int is not changed of course (not sure what that means anyway!)
When you print the size using sizeof, you are printing a number that is the size of the short int (and the number is of type size_t). printf() doesn't even see the short int, sizeof operator does, and reports the correct size.
The point of creating a short variable is that if you want a short variable, you create one. This is true for most variables of course :-). But if you don't think you need a short int specifically, it's okay to just use int.
If you call a function without a prototype or a function with variable arguments, like printf(3), then C applies something called the default argument promotions.
These conversions promote float to double and anything smaller than int to int or unsigned int. This tends to harmonize most of the types.
This is an interesting feature that, possibly, C introduced to the world. It actually happens to some extent at the instruction set level or ABI level. Parameters are passed in registers or on the stack, and typically no one allows misaligning the stack or leaving junk in higher-order bits.
Just one more reason why C matches the hardware so well and runs so fast.
This conversion happens in the call to printf, because for variadic functions, all the arguments passed in as part of the ... get widened to int (or double, if the argument is a float) first.

Resources