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.
Related
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");
I started learning programming only few days ago, so basically I have no knowledge.
I'm starting with C, and I wrote a very simple code which is:
int main (int argc, const char * argv[])
{
printf("%d + %d", 1 + 3);
return 0;
}
with the code above, I got the value of 4 + 1606416608 and later found that the return value is wrong because I put more %d than necessary. Then my question is, how did that strange value actually come out? If anyone knows, please help me. Thank you!!
You know what you did wrong already, so to explain what your particular implementation of C probably did:
When you call printf, a new stack frame is pushed to the call stack. The call stack is a last in first out structure with one 'frame' per called function. So if main called logStuff which called printf then three consecutive frames would be for main, then logStuff, then printf. When printf returns, it's frame is removed from the structure and execution continues with logStuff.
So a frame usually contains at least the parameters passed to the function and storage for local variables. Those things may be one and the same, it's implementation dependant.
With a variadic function like printf there's a stream of unnamed parameters. The bit patterns will be put into an appropriate place in the frame. But C is not a reflective language. Each bit patten doesn't inherently have a meaning: any one could be an integer, a float, or anything else. It also isn't a language that invests in bounds checking. You're trusted to write code that acts correctly.
printf determines the types and number of unnamed parameters from the string. So if you've given it false information, it will interpret the bit patterns with something other than their correct meaning and it may think there are fewer or more than are really there.
You told it there were more. So what probably happened was that the parameters were in the equivalent of an array and it read a value from beyond the end of the array. As it's all implementation dependent, that value may have been meant to represent anything. It could be the address of the caller. It could be uninitialised storage for another local variable. It could be bookkeeping. It could be the format string, incorrectly interpreted as an integer.
What it isn't is any reliable value. It may not even always be safe to read.
You are in undefined behavior land... you are telling a variadic function that you have 2 int sized params, then you only supply one, you are leaking something from the stack.
1) %d is a format specifier, it tells the compiler how you want to access the value stored at a particular location.(here as an integer)
2) For every format specifier you need to provide a corresponding variable or a value, otherwise at runtime you will get "garbage" i.e. some random value.
Example :
int main()
{
int a = 65;
printf("\na = %d", a); // here the value stored in a is accessed as an integer.
printf("\na = %c", a); // the value inside a is accessed as a character.
return 0;
}
In the above example '%d' in the first printf statement tells the compiler that the value stored in the variable a is to be accessed as an integer. (o/p - 65)
In the second printf statement '%c' is used to access the same variable as a character.(o/p - A)
Your code expects two numerical parameters to be printed, and you're giving it one.
Expected:
printf("%d + %d", <some_num>, <another_num>);
You're giving it:
printf("%d + %d", <some_num>);
Where <some_num> is what 1+3 evaluates to. The function expects another argument, but receives garbage instead.
What you should do is
printf("%d + %d = %d", 1, 3, 1+3);
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 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.
What's going on here:
printf("result = %d\n", 1);
printf("result = %f\n", 1);
outputs:
result = 1
result = 0.000000
If I ensure the type of these variables before trying to print them, it works fine of course. Why is the second print statement not getting implicitly converted to 1.00000?
In the second case you have a mismatch between your format string and the argument type - the result is therefore undefined behavio(u)r.
The reason the 1 is not converted to 1.0 is that printf is “just” a C function with a variable number of arguments, and only the first (required) argument has a specified type (const char *). Therefore the compiler “cannot” know that it should be converting the “extra” argument—it gets passed before printf actually reads the format string and determines that it should get a floating point number.
Now, admittedly your format string is a compile-time constant and therefore the compiler could make a special case out of printf and warn you about incorrect arguments (and, as others have mentioned, some compilers do this, at least if you ask them to). But in the general case it cannot know the specific formats used by arbitrary vararg functions, and it's also possible to construct the format string in complex ways (e.g. at runtime).
To conclude, if you wish to pass a specific type as a “variable” argument, you need to cast it.
An undefined behavior. An int is being treated as float
The short answer is that printf isn't really C++. Printf is a C function which takes a variable argument list, and applies the provided arguments to the format string basis the types specified in the format string.
If you want any sort of actual type checking, you should use streams and strings - the actual C++ alternatives to good old C-style printf.
Interesting, presumably it's fine if your put '1.0'
I suppose the printf only gets the address of the variable, it has no way of knowing what it was. But I would have thought the compiler would have the decency to warn you.