0

Here is my program:

#include <stdio.h>
int main()
{
    int a=0x09;
    int b=0x10;
    unsigned long long c=0x123456;
    printf("%x %llx\n",a,b,c);//in "%llx", l is lowercase of 'L', not digit 1
    return 0;
}

the output was:

9 12345600000010

I want to know:

  1. how function printf() is executed?
  2. what will happen if the number of arguments isn't equal to that of formats?

please help me and use this program as an example to make an explanation.

2
  • 2
    This is undefined behaviour. There's little point in asking why it does what it does if you don't use an assembly level debugger. Commented Sep 18, 2011 at 9:31
  • See stackoverflow.com/questions/2424528/… Commented Sep 18, 2011 at 9:57

3 Answers 3

2

The problem is that your types don't match. This is undefined behavior.

Your second argument b does not match the type of the format. So what's happening is that printf() is reading past the 4 bytes holding b (printf is expecting an 8-byte operand, but b is only 4 bytes). Therefore you're getting junk. The 3rd argument isn't printed at all since your printf() only has 2 format codes.

Since the arguments are usually passed consecutively (and adjacent) in memory, the 4 extra bytes that printf() is reading are actually the lower 4 bytes of c.

So in the end, the second number that's being printed is equal to b + ((c & 0xffffffff) << 32).

But I want to reiterate: this behavior is undefined. It's just that most systems today behave like this.

Sign up to request clarification or add additional context in comments.

4 Comments

Passing more arguments than there are format specifiers does not cause UB, they are just ignored.
Correct, though I think it's still bad practice to do so.
I don't agree that it's necessarily bad practice. You might have a diagnostic formatting utility that might be configured to show additional details in some configurations. E.g. printf( ERRSTR, message, fn_name ). Normally, you might just want users to see "Error: %s\n" but in development or when configured for detailed diagnostics you might use "Error: %s in function %s\n". Not having to change the printf call is a useful feature, why not use it?
I've never seen printf() used that way, but I do get your and point and I see how that can be useful.
1

If the arguments that you pass to printf don't match the format specification then you get undefined behavior. This means that anything can happen and you cannot reason about the results that you happen to see on your specific system.

In your case, %llx requires and argument of type unsigned long long but you supplied an int. This alone causes undefined behaviour.

It is not an error to pass more arguments to printf than there are format specificiers, the excess arguments are evaluated but ignored.

Comments

0

printf() increases a pointer to read an argument at a time according to the format. If the number of formatting arguments is larger than the number of parameters, then printf() will output data from unknown memory locations. But if the number of parameters is larger than the number of formatting arguments, then no harm was done. E.g. gcc will warn you if the number of formatting arguments and parameters don't match.

4 Comments

"If the number of formatting arguments is larger...". This is simply not true, excess arguments are just ignored. It is only UB if you fail to supply enough arguments or the supplied arguments are not the correct types for the format specifiers.
I thought that "..." works by setting a pointer to the first vararg argument which is then increased by printf (or any other vararg function for that matter) as needed. Once the pointer runs out of the parameters provided, the pointer increment would point to an undefined memory location. Any other implementation would require either null terminated list of arguments, or getting the number of parameters. But since there are vararg functions (e.g. g_object_set()) that require terminating a list with NULL, this doesn't make sense.
Perhaps I misunderstood you. I think you might have used "formatting arguments" to refer to "conversion specifications" whereas I took the word argument to mean that you were referring to actual function arguments to printf.
Yes, rereading what i wrote I realized that I made a mess of terminology. I should be more pedantic. Thanks for clearing this up.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.