Why does printf( %f ,0); give undefined behavior?
The "%f"
format requires an argument of type double
. You're giving it an argument of type int
. That's why the behavior is undefined.
The standard does not guarantee that all-bits-zero is a valid representation of 0.0
(though it often is), or of any double
value, or that int
and double
are the same size (remember it's double
, not float
), or, even if they are the same size, that they're passed as arguments to a variadic function in the same way.
It might happen to "work" on your system. That's the worst possible symptom of undefined behavior, because it makes it difficult to diagnose the error.
N1570 7.21.6.1 paragraph 9:
... If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.
Arguments of type float
are promoted to double
, which is why printf("%f\n",0.0f)
works. Arguments of integer types narrower than int
are promoted to int
or to unsigned int
. These promotion rules (specified by N1570 6.5.2.2 paragraph 6) do not help in the case of printf("%f\n", 0)
.
Note that if you pass a constant 0
to a non-variadic function that expects a double
argument, the behavior is well defined, assuming the function's prototype is visible. For example, sqrt(0)
(after #include <math.h>
) implicitly converts the argument 0
from int
to double
-- because the compiler can see from the declaration of sqrt
that it expects a double
argument. It has no such information for printf
. Variadic functions like printf
are special, and require more care in writing calls to them.
Is it undefined behavior to call printf with %s and pass a zero-length char*?
Short answer: It is Undefined Behavior
Long answer: In C++, allocating an array of size 0
will produce a valid pointer to an array with no elements.
From the standard (taken from this answer):
From 5.3.4/7
When the value of the expression in a direct-new-declarator is zero, the allocation function is called to allocate an array with no elements.
From 3.7.3.1/2
The effect of dereferencing a pointer returned as a request for zero size is undefined.
(Emphasis mine)
This means that there is no way to properly read from (or write to) the pointer returned from a new T[0]
request.
Both strlen
and printf
for string formatting "%s
" are defined to work on strings of characters that are terminated by a special NUL
character. They require reading the sequence of characters from the supplied pointer to try to find this NUL
character in order to properly operate (which results in UB, since this requires dereferencing the pointer). These behaviors are defined in the C standard, since the C++ standard delegates definitions of most C library types/functions back to the C standard.
printf
access for %s
is defined to do the following:
From C11 Standard §7.21.6.1/6
If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type.
Characters from the array are written up to (but not including) the terminating null character. If the precision is specified, no more than that many bytes are written. If the precision is not specified or is greater than the size of the array, the array shall contain a null character.
This requires access to the array (which will be UB, since the pointer is not valid to dereference)
Bonus
Your sample code is actually introducing UB on the second line due to the use of strlen
, for similar reasons to above.
strlen
is defined to do the following:
From C11 Standard §7.24.6.3/3: The strlen
function
Returns
The strlen function returns the number of characters that precede the terminating null character.
Which is UB for the same reason as using printf
.
Why does printf print wrong values?
well of course it prints the "weird" stuff. You are passing in int
s, but telling printf
you passed in float
s. Since these two data types have different and incompatible internal representations, you will get "gibberish".
There is no "automatic cast" when you pass variables to a variandic function like printf
, the values are passed into the function as the datatype they actually are (or upgraded to a larger compatible type in some cases).
What you have done is somewhat similar to this:
union {
int n;
float f;
} x;
x.n = 10;
printf("%f\n", x.f); /* pass in the binary representation for 10,
but treat that same bit pattern as a float,
even though they are incompatible */
what does the output of this code mean? printf( %d , x );
I want to make it clear as other have said that this code is wrong because it has undefined behavior so could do absolutely anything on your platform. Don't do this... However to aid understanding :-
"x"
is a string constant and this decays to a pointer to the string and passed as a parameter to printf
. You've asked it to print an "int" so it interprets the bytes of the address as if they were an int
and prints those.
In practice this will print the address that the string is stored at as a decimal number.
But nothing in the standard guarantees this, it could do anything, and is likely to differ on other platforms. In general though, due to the way C is implement though in most cases it will do what I've described.
Note than on some 64 bit platforms an int is 32 bits but an address is 64 bits so this is likely to at best only print an integer based on half the bytes of the address.
As others have said it's undefined and bad so don't do this, but it's still useful to understand what it's actually doing.
Edit: As suggested... If you want to interpret the data passed properly use %p
instead of %d
which tells printf
to interpret the data passed as a memory address rather than an int
. Although the format is system dependent it's likely to be print the same int
, and is properly defined behavior then
Why printf( %.1f , 1) output 0.0
Using wrong format specifier invokes undefined behavior. You may get either expected or unexpected result. Use %d
instead as the argument passed to printf
is int
type or change 1
to 1.0
if you are using %f
.
C11: 7.21.6 (p9):
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.
Related Topics
Why Doesn't My Template Accept an Initializer List
Converting String of 1S and 0S into Binary Value
Connected Components in Opencv
Tackling Class Imbalance: Scaling Contribution to Loss and Sgd
How to Cout a Float Number with N Decimal Places
False Positive with Is_Copy_Constructible on Vector<Unique_Ptr>
C++ Error: Undefined Reference to 'Main'
What Is the Optimal Algorithm for Generating an Unbiased Random Integer Within a Range
What Is Forward Declaration in C++
Cout or Printf Which of the Two Has a Faster Execution Speed C++
Conversion from String Literal to Char* Is Deprecated
Changing the Value of Const Variable in C++
C++: Construction and Initialization Order Guarantees
Unsequenced Value Computations (A.K.A Sequence Points)
Why Don't the C or C++ Standards Explicitly Define Char as Signed or Unsigned
Default Class Inheritance Access