What is the function definition of the printf() function as defined in the standard C library?
I need the definition to solve the following question:
Give the output of the following:
int main()
{
int a = 2;
int b = 5;
int c = 10;
printf("%d ",a,b,c);
return 0;
}
The C language standard declares printf as follows:
int printf(const char *format, ...);
It returns an integer and takes a first parameter of a pointer to a constant character and an arbitrary number of subsequent parameters of arbitrary type.
If you happen to pass in more parameters than are required by the format string you pass in, then the extra parameters are ignored (though they are still evaluated). From the C89 standard §4.9.6.1:
If there
are insufficient arguments for the format, the behavior is undefined.
If the format is exhausted while arguments remain, the excess
arguments are evaluated (as always) but are otherwise ignored.
You pass an array of chars (or pointer) as the first argument (which includes format placeholders) and additional arguments to be substituted into the string.
The output for your example would be 2 1 to the standard output. %d is the placeholder for a signed decimal integer. The extra space will be taken literally as it is not a valid placeholder. a is passed as the first placeholder argument, and it has been assigned 2. The extra arguments won't be examined (see below).
printf() is a variadic function and only knows its number of additional arguments by counting the placeholders in the first argument.
1 Markdown does not allow trailing spaces in inline code examples. I had to use an alternate space, but the space you will see will be a normal one (ASCII 0x20).
Its
int printf(const char *format, ...);
format is a pointer to the format string
... is the ellipsis operator , with which you can pass variable number of arguments, which depends on how many place holders we have in the format string.
Return value is the number of characters that were printed
Have a look here about the ellipsis operator: http://bobobobo.wordpress.com/2008/01/28/how-to-use-variable-argument-lists-va_list/
man 3 printf gives...
int printf(const char *restrict format, ...);
Writes to the standard output (stdout) a sequence of data formatted as the format argument specifies. After the format parameter, the function expects at least as many additional arguments as specified in format.
%d = Signed decimal integer
printf("%d ",a,b,c);
For every %(something) you need add one referining variable, therefore
printf("%d ",a+b+c); //would work (a+b+c), best case with (int) before that
printf("%d %d %d",a,b,c); //would print all 3 integers.
Related
What is the behavior of printf() when we supply multiple arguments to it without a format specifier?
Example:
int main()
{
printf("hello", "hi");
return 0;
}
Why does the compiler produce a warning on compilation of the above program? :
warning: too many arguments for format [-Wformat-extra-args]
If we compile the similar program below:
int main()
{
char *s1 = "hello";
char *s2 = "hi";
printf(s1, s2);
}
No warnings are produced. What is the reason for this?
Also, why do both programs output hello only, and don't also print hi?
The C 2018 standard specifies the behavior of printf in clause 7.21.6.3, in which paragraph 2 says “The printf function is equivalent to fprintf with the argument stdout interposed before the arguments to printf.”
The standard specifies the behavior of fprintf in 7.21.6.1, which tells us the second argument (the first argument of printf) is a format string and that it may contain various conversion specifications introduced by the character “%”. Thus, in printf("hello", "hi"), "hello" is a format string with no conversion specifications. In this case, paragraph 2 tells us what happens:
If the format is exhausted [fully processed] while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored.
Thus, in printf("hello", "hi"), "hi" is ignored, and "hello" is a format string that contains only ordinary characters, which are copied to the output stream per paragraph 3.
The compiler warns about printf("hello", "hi") because it is able to see that this call contains an excess argument because the format string does not contain a conversion specification for it.
Your compiler does not warn about printf(s1,s2); because it does not analyze what s1 will contain during this call. This sort of analysis is not impossible in this situation, but situations like this are rare: When a programmer uses a pointer to a string as the format string for printf, it is usually a string or pointer that is computed, constructed, or selected during program execution, and the manner of this computation is often beyond the ability of a compiler to analyze. Situations where the pointer is clearly a pointer to a fixed string are rare, since they are not frequently useful, so presumably compiler implementors have not found it valuable to implement the code necessary for the compiler to handle these situations.
tl;dr: Extra arguments to printf() are ignored.
The official C language standard (the link is to a draft of the C11 version) says the following:
§ 7.21.6.1 The fprintf function
...
... If the format is exhausted while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored. The fprintf function returns when the end of the format string is encountered.
... and printf() is simply fprintf() targeted at the standard output file.
About your two code snippets:
The compiler is giving you a hint, for the first snippet, that the number of arguments doesn't match the number of specifiers in the format string. It's just a courtesy - it's not required to notice this. This also explains why the compiler does not notice it for the second snippet. It could, but it's too much effort to chase your pointers and check what they point at.
In both cases, your format string is your first argument to printf(), i.e. "hello". That string has no format specifiers, so the printf() looks at the "hello", and understands it only needs to print that and doesn't need process any other arguments. That's whi it ignores "hi".
The first parameter of printf is the format string, because printf is about printing formatted data. To specify how to format the data, printf uses the first argument. This is different from other languages and libraries where all the parameters (like Python's print) are used in the same way and formatting is done through other means.
The first and second examples you provide are both "incorrect" although technically valid because you are passing a format string that does not need any extra argument, so "hi" is unused.
What you may want to do instead is:
printf("%s %s", "hello", "hi");
many compilers know well very well the printf function family and read compile time the format string analysing the parameters. printf("hello",s2); compiler see that there is no %... in the format string and does not expect any other parameters. Warning is issued
if you call printf(s1,s2); compiler does not know what is the content of the s1 and it cannot go through the format string and no warning issued.
Many compilers have special extension to inform them that your function is printf like and you want compiler to read the format string - gcc:
extern int
my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));
In function with variable number of arguments, the "first argument" is "total number of arguments" being passed. But in printf() we never mention argument count. So how does it get to know about the total argument list ? How does printf() works ?
Let's look on printf declaration structure:
int printf(const char *format, ...)
format is actually the string that contains the text to be written to stdout.
The contained embedded format tags are later replaced by the values specified in subsequent additional arguments, and format is set accordingly as required.
You don't supply an argument count to printf - You do supply a format string, however - And that specifies how many arguments printfshould expect.
Very roughly speaking, the number of % signs in the format string is the argument count (although reality is a bit more complicated).
Conversion specifiers in the format string tell printf the number and types of arguments it should expect - for example, the format string "there are %d vowels in %s\n" tells printf to expect two arguments in addition to the format string, the first one being type int and the second being char *.
It's up to you to make sure the arguments match up with the format string. If you don't pass enough arguments, or the argument types don't match what the format string expects, then the behavior is undefined (most likely garbled output or a runtime error). If you pass too many arguments, the additional arguments are evaluated, but otherwise the function will operate normally as long as the format string is satisfied.
Edit
Chapter and verse
7.21.6.1 The fprintf function
...
2 The fprintf function writes output to the stream pointed to by stream, under control of the string pointed to by format that specifies how subsequent arguments are converted for output. If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored. The fprintf function returns when the end of the format string is encountered.
I wrote a simple program with a single printf statement,like hello world.
#include <stdio.h>
#define MAX 100
int main()
{
printf("Max is %d\n",MAX);
}
I studied that macros are just substituted in place of occurrence ,by preprocessor. Generally printf need a variable name with corresponding format specifier to print the value of variable.
Here ,with my understanding, 100 should be replaced in printf call and should raise an error.
But the output is :
Max is 100
How and why?
"Generally printf() need a variable name with corresponding format specifier to print the value of variable."
There you went wrong. All the format specifiers supplied with printf() expects an argument of the particular type, not a variable of that type. 100, an integer literal, is a valid argument for %d, in this case.
So, printf("Max is %d\n",100); is both a valid and legal statement in C. The output you got is expected output, there should be no error or warning with this.
Just to add a reference to the actual words, quoting C11, chapter §7.21.6.1, fprintf() (emphasis mine)
d,i
The int argument is converted to [...]
The printf format "%d" tells printf to extract an int argument from the argument list. If that int comes from a variable or a literal doesn't matter.
I think the title does not suit well for my question. (I appreciate it, if someone suggests an Edit)
I am learning C with "Learn C The Hard Way.". I am using printf to output values using format specifiers. This is my code snippet:
#include <stdio.h>
int main()
{
int x = 10;
float y = 4.5;
char c = 'c';
printf("x=%d\n", x);
printf("y=%f\n", y);
printf("c=%c\n", c);
return 0;
}
This works as I expect it to. I wanted to test it's behavior when it comes to conversion. So everything was ok unless I made it to break by converting char to float by this line:
printf("c=%f\n", c);
Ok, I'm compiling it and this is the output:
~$ cc ex2.c -o ex2
ex2.c: In function ‘main’:
ex2.c:13:3: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf("c=%f\n", c);
^
The error clearly tells me that It cannot convert from int to float, But this does not prevent the compiler from making an object file, and the confusing part is here, where I run the object file:
~$ ./ex2
x=10
y=4.500000
c=c
c=4.500000
As you can see printf prints the last float value it printed before. I tested it with other values for y and in each case it prints the value of y for c. Why this happen?
Your compiler is warning you about the undefined behaviour you have. Anything can happen. Anything from seeming to work to nasal demons. A good reference on the subject is What Every C Programmer Should Know About Undefined Behavior.
Normally, int can convert to double just fine:
int i = 10;
double d = i; //works fine
printf is a special kind of function. Since it can take any number of arguments, the types have to match exactly. When given a char, it is promoted to int when passed in. printf, however, uses the %f you gave it to get a double. That's not going to work.
Here is how one would implement their own variadic function, taken from here:
int add_nums(int count, ...)
{
int result = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
count is the number of arguments that follow. There is no way for the function to know this without being told. printf can deduce it from the format specifiers in the string.
The other relevant part is the loop. It will execute count times. Each time, it uses va_arg to get the next argument. Notice how it gives va_arg the type. This type is assumed. The function needs to rely on the caller to pass in something that gets promoted to int in order for the va_arg call to work properly.
In the case of printf, it has a defined list of format specifiers that each tell it which type to use. %d is int. %f is double. %c is also int because char is promoted to int, but printf then needs to represent that integer as a character when forming output.
Thus, any function that takes variadic arguments needs some caller cooperation. Another thing that could go wrong is giving printf too many format specifiers. It will blindly go and get the next argument, but there are no more arguments. Uh-oh.
If all of this isn't enough, the standard explicitly says for fprintf (which it defines printf in terms of) in C11 (N1570) §7.21.6.1/9:
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
All in all, thank your compiler for warning you when you are not cooperating with printf. It can save you from some pretty bad results.
Since printf is a varargs function, parameters cannot be converted automatically to the type expected by the function. When varargs functions are called, parameters undergo certain standard conversions, but these will not convert between different fundamental types, such as between integer and float. It's the programmer's responsibility to ensure that the type of each argument to printf is appropriate for the corresponding format specifier. Some compilers will warn about mismatches because they do extra checking for printf, but the language doesn't allow them to convert the type -- printf is just a library function, calls to it must follow the same rules as any other function.
Here is a very general description, which may be slightly different depending on the compiler in use...
When printf("...",a,b,c) is invoked:
The address of the string "..." is pushed into the stack.
The values of each of the variables a, b, c are pushed into the stack:
Integer values shorter than 4 bytes are expanded to 4 bytes when pushed into the stack.
Floating-point values shorter than 8 bytes are expanded to 8 bytes when pushed into the stack.
The Program Counter (or as some call it - Instruction Pointer) jumps to the address of function printf in memory, and execution continues from there.
For every % character in the string pointed by the first argument passed to function printf, the function loads the corresponding argument from the stack, and then - based on the type specified after the % character - computes the data to be printed.
When printf("%f",c) is invoked:
The address of the string "%f" is pushed into the stack.
The value of the variable c is expanded to 4 bytes and pushed into the stack.
The Program Counter (or as some call it - Instruction Pointer) jumps to the address of function printf in memory, and execution continues from there.
Function printf sees %f in the string pointed by the first argument, and loads 8 bytes of data from the stack. As you can probably understand, this yields "junk data" in the good scenario and a memory access violation in the bad scenario.
I was reading a book and came across a program to read entries from a /proc file.
The program which they mentioned has following line
printf("%.*s", (int) n, line);
I am not clear with meaning of above line
what type of print if above "%.*s used instead of %s
The code can be read here
Abstract from here:
.* - The precision is not specified in
the format string, but as an
additional integer value argument
preceding the argument that has to be
formatted.
So this prints up to n characters from line string.
The cast expression (int) n converts the value of n to type int. This is because the formatting specifier requires a plain int, and I assume (since you didn't include it) the variable n has a different type.
Since a different type, like size_t might have another size, it would create problems with the argument passing to printf() if it wasn't explicitly converted to int.