Printing with format specifiers in C - 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");

Related

Am I using conversion specifiers wrong or something? scanf() isn't working in c

scanf() isnt working properly when I use the following lines of code in c:
double withdraw = 0;
scanf("%.2lf",&withdraw);
printf("\n\nWithdraw %.2lf?",&withdraw);
and this:
double withdraw = 0;
scanf("%.2lf",withdraw);
printf("\n\nWithdraw %.2lf?",withdraw);
and this:
double withdraw = 0;
scanf("%.2lf",&withdraw);
printf("\n\nWithdraw %.2lf?",withdraw);
and finally this:
double withdraw = 0;
scanf("%.2lf",withdraw);
printf("\n\nWithdraw %.2lf?",&withdraw);
input into scanf() is ALWAYS this:
1
output of withdraw is ALWAYS this:
0.00
Please help. This seems so crazy to me!
Yes, your code is wrong. You cannot use precisions (".<int>") with fscanf() family, that's invalid. This causes causes undefined behavior.
Quoting C11, chapter §7.21.6.2/p3, fscanf()
Each conversion specification is introduced by the character %.
After the %, the following appear in sequence:
— An optional assignment-suppressing character *.
— An optional decimal integer greater than zero that specifies the maximum field width
(in characters).
— An optional length modifier that specifies the size of the receiving object.
— A conversion specifier character that specifies the type of conversion to be applied.
and, p13
If a conversion specification is invalid, the behavior is undefined.
That said, do not use trial and error, undefined behaviors are tricky to catch and understand. read the manual pages for proper format specifier for any argument type.
As your compiler is telling you
warning: unknown conversion type character ‘.’ in format [-Wformat=]
So you cannot specify precision with this type of format specifier
The following could work.
#include <stdio.h>
#include <math.h>
int main()
{
double withdraw = 0;
scanf("%lf", &withdraw);
printf("\n\nWithdraw %.2lf?", withdraw);
return 0;
}

Macro variable substitution in printf

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.

Why compiler is not issuing error while passing an int (not int *) as the argument of scanf()?

I tried the below c program & I expected to get compile time error, but why compiler isn't giving any error?
#include <stdio.h>
#include <conio.h>
int main()
{
int a,b;
printf("Enter a : ");
scanf("%d",&a);
printf("Enter b : ");
scanf("%d",b);
printf("a is %d and b is %d\n",a,b);
getch();
return 0;
}
I am not writing & in scanf("%d",b). At the compile time , compiler don't give any error but during execution value of b is 2686792(garbage value).
As per C11 standard, Chapter §6.3.2.3
An integer may be converted to any pointer type. Except as previously specified, the
result is implementation-defined, might not be correctly aligned, might not point to an
entity of the referenced type, and might be a trap representation.
So, the compiler will allow this, but the result is implementation-defined.
In this particular case, you're passing b to scanf() as an argument, which is uninitialized, which will cause the program to invoke undefined behaviour, once executed.
Also, printf() / scanf() being variadic functions, no check on paramater type is perforfmed in general, unless asked explicitly through compiler flag [See -Wformat].
It is not a a compile time error but a runtime issue.
The compiler expects that you have given a valid address to scan the value to, during runtime only it will come to know whether the address is valid or not .
If you try to scan the value to invalid address it leads to undefined behavior and might see a crash.
It scans fine because scanf expects a memory location in its argument. That's why we use & to give memory location of the corresponding variable.
In your case scanf just scans the entered value and puts it in the memory location which has the value of b(instead of scanning and putting it in the memory location where b is stored).
The & operator in C returns the address of the operand. If you give just the b without the & operator, it will be passed by value and the scanf won't be able to set the value. This would result in unexpected behaviour as you already noticed. It has no way of verifying the address before the runtime and hence you won't notice the issue until runtime.

C variable assignment issue

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.

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.

Resources