Macro variable substitution in printf - c

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.

Related

Pass string literal as the argument to the print function with out any format specifier?

Why I am not getting error even though I am not not passing any format specifier but passing a string literal. There is no error in the case of string literal but there is an error in the case of character,integer. Why ?
#include<stdio.h>
int main()
{
printf("Hello World");
return 0;
}
The first argument to printf should be a format string1. "Hello World" is a format string2.
Per paragraph 7.21.6.1 3 in the C 2018 standard, the format string is composed of zero or more directives:
A % character starts a directive for a conversion specification.
Any other character is a directive to output that character unchanged.
So "Hello World" is a format string that says to print “H”, “e”, “l”, “l”, “o”, “ ”, “W”, “o”, “r”, “l”, and “d”. It is simply a format string with only ordinary characters, no conversion specifications. It is the proper type and data for the first parameter of printf, so no errors occurs.
In contrast, when a char or int is passed as the first argument to printf, the compiler knows it is the wrong type for the argument and issues a warning or error message.
Footnotes
1 Technically, the argument should be a pointer to the first character of the format string.
2 "Hello World" is passed as a pointer because, while it is an array of characters, it is automatically converted to a pointer to its first character.
The documentation on printf is as follows:
int printf ( const char * format, ... );
Print formatted data to stdout
Writes the C string pointed by format to the standard output (stdout). If format includes format specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers.
putting a char or int variable in place of format above will fail
It is possible with a help of preprocessor and Generic Selection introduced in C11 to select a proper format string basing on argument's type.
Try:
#define FMT(X) _Generic(X, int: "%d", char*: "%s", float: "%f")
#define print(X) printf(FMT(X), (X))
Now the program:
int main() {
print(1);
print("abc");
print(3.0f);
return 0;
}
Produces an expected output of 1abc3.00000.

Printing with format specifiers in C

In a class quiz, I was asked to write the output of the below code snippet.
x = 1.234;
printf("x=%2d");
The variable x contains point values so I assumed it to be a float/double type.
In the paper I answered that this code would just simply print the statement within quotes (as x=%2d), as in print function it prints whatever within the " " as it is.
But later I ran the code in my compiler to find the output as x=4199232 (The number varied in different compilers though)
(Edit- I added the compiled code here)
#include <stdio.h>
int main() {
float x = 1.234;
printf("x=%2d");
return 0;
}
Can anybody kindly explain me what is really happening here.
The code has undefined behavior (which explains why the number varied in different compilers) because you do not provide an argument of type int for the conversion %2d.
If you had written this:
x = 1.234;
printf("x=%2d", x);
The output would depend on the type of x which does not appear in the code fragment. If x is defined with type int or a smaller integer type, including _Bool, the output should be x= 1, but if x has any other arithmetic type, including float and double, the behavior is again undefined because the argument does not have the expected type for %d.
Note also that there is no trailing \n in the format string, so the output might be delayed until the end of the program and might not appear at all on some non conformant systems.
In your sample code, the behavior is undefined because of the missing argument to printf, but you do define x as a float, which would be implicitly converted as a double when passed to printf, invalid for %d.
Here is a modified version:
#include <stdio.h>
int main() {
double x = 1.234; // only use `float` when necessary
printf("x=%2d\n", (int)x); // outputs `x= 1`
printf("x=%2f\n", x); // outputs `x=1.234000`
printf("x=%2g\n", x); // outputs `x=1.234`
printf("x=%.2f\n", x); // outputs `x=1.23`
return 0;
}
In this statement
printf("x=%2d");
you forgot to specify an argument for the conversion specifier d. So the program will try to output whatever is stored in the memory where the second argument should be.
So the program has undefined behavior.
It will also have undefined behavior if you will specify the second argument like
printf("x=%2d", x );
because there is used an invalid conversion specifier with an object of the type float.
To output just the format string you should write
printf("x=%%2d");

Why are conversion characters needed?

When I run this:
#include <stdio.h>
int main() {
int x = 1;
printf(x, "\n");
return 0;
}
It gives me these errors:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x1)
Format string is not a string literal (potentially insecure)
Treat the string as an argument to avoid this
Incompatible integer to pointer conversion passing 'int' to parameter of type 'const char *'
And it outputs:
(lldb)
However, when I change it to:
printf("%s", x);
It works perfectly fine. (outputs "1", as expected)
Why are the conversion characters (e.g. %s, %d, etc...) needed?
They're needed to tell the printf function what the types (and number) of the parameters are that you are passing.
The language has no mechanism to allow a function to determine this dynamically, so the format string gives it the clues to decode them.
The format string is always the first parameter, because the called function can always access that in the same place. Functions like printf are still written in C typically, so can only use those functions that the language provides.
They parameters are not "conversion characters". I think your confusion comes from the fact that you think printf simply prints all its arguments and automatically deduces how to print each. However, printf cannot work like that because C does not have support for overloading.
Concretely, the first argument is not like the rest. It is not something to print, but a format string. You can see easily what it means by trying this:
printf("My friend %s has %d coins!", "John", 123);
which will print:
My friend John has 123 coins!
%s here specifies the first argument (after the format string) will be interpreted as a string, and %d means the second argument (again, after the format string) will be understood as an integer. Both will be replaced with the actual value from the argument.

why does printf() gives random output when it should be 0?

so as printf() is a function and it returns the number of characters written if successful or negative value if an error occurred, looking at this example, the output as expected is zero.
#include <stdio.h>
int main(void)
{
printf("%d");
return 0;
}
now when I add some more of these %d : http://ideone.com/brw5vG
the output changes to this:
0 134513819 -1216430092 134513808
I am not able to figure out whats up with the random garbage values? There is a negative value here in the output as well, and a negative value justifies an error, so can anyone pinpoint what is the error here exactly?
Please be concise and specific. Thanks.
Because "%d" means an integer is expected. You don't pass any, so you get undefined behaviour.
Note that g++ 4.8.2 gives a useful warning:
warning: format '%d' expects a matching 'int' argument [-Wformat=]
similarly for clang++ 3.4:
warning: more '%' conversions than data arguments [-Wformat]
the output as expected is zero
printf("%d");
You should not expect anything as your program invokes undefined behavior.
(C99, 7.19.6.1p2) "[...] If there are insufficient arguments for the format, the behavior is
undefined.[...]"
You are mis-specifiying the format string to printf this is undefined behavior and you should have no expectations as to the result. By specifying %d you are telling printf to expect an int argument which you have not provided.
If we look at the C99 draft standard section 7.19.6.1 The fprintf function which also covers pritnf with respect to format specifiers says:
[...]If there are insufficient arguments for the format, the behavior is undefined.[...]
The problem is in how you pose the question; you assume it "should be 0." The fact is that this is undefined behavior, and printf will substitute for %d whatever happens to be in the stack.
Your code invokes undefined behavior. Anything could be happen.
The C11 Standard says in section 7.21.6 Formatted input/output functions:
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
You are passing no argument for the corresponding %d specifier.
There are 2 issues at hand: First is why doesn't the compiler issue an error about this bad call to printf(), and second is why you get garbage output. I'll answer them one at a time.
printf() is a tricky function. While most functions have a constant amount of arguments passed to them, printf() is different.
For example, if we take this simple function:
int max(int a, int b) {
if (a > b) return a;
else return b;
}
You can see that this function always receives 2 arguments. This is also something that the compiler knows, and enforces when you compile you code. This is why a call such as max(4) won't work. The compiler will see that we are passing max() 1 argument instead of 2 and it will issue an error.
printf() is a function that takes a variable amount of arguments, and this amount is determined by the amount of format specifiers (things that start with %) in the format string. This means that the compiler cannot know at compile time if the amount of arguments that you passed to printf is enough (or maybe too much).
The reason that you get garbage printed is because of how functions read their input arguments. All input arguments for a function reside on the stack. These are pushed into the stack before the function is called and later addressed by the function. In this case, printf() expects to have an extra argument besides the format string (because of the %d), and so it looks in the address where its 2nd argument might have been. Alas, that argument wasn't passed, so it will actually look into a place in the stack that might contain anything else (a return address, a frame pointer, a local variable of an enclosing scope or other).
You can read more about how function calls work here.

What's the standard definition of printf in C?

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.

Resources