Why Does Printf("%F",0); Give Undefined Behavior

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 ints, but telling printf you passed in floats. 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



Leave a reply



Submit