This question already has answers here:
Behaviour of printf when printing a %d without supplying variable name
(6 answers)
Closed 4 years ago.
I stumbled upon this C code in a college test and when I tested it on Dev-C++ 5.11 compiler, it printed random characters. I can't understand how or why this code works. Can someone enlighten me?
int main() {
char a[10] = "%s" ;
printf( a ) ;
}
This question has two parts: the missing quotes and the random characters.
printf() is just a function. You can pass strings and other values to functions as arguments. You don't have to use a literal. You can use both char *a = "something"; printf(a) (passing a variable as an argument) and printf("something") (passing a string literal as an argument).
printf() is also a variadic function. This means it can accept any number of arguments. You can use printf("hello world"), printf("%s", "hello world") and even printf("%s %s", "hello", "world"). Some older compilers don't verify you actually passed the right number of arguments based on the first argument which is the format string. This is why your code compiles even though it's missing an argument. When the program runs the code goes over the format string, sees "%s" and looks for the second argument to print it as a string. Since there is no second argument it basically reads random memory and you get garbage characters.
printf function signature is:
int printf(const char *format, ...);
It expects format string as the first argument and variable number of arguments that are handled and printed based on the format specifiers in the format string. variable a in your question is providing it the format string. Reason for random characters is that the argument for format specifier %s is missing. Following will correctly print a string:
printf( a, "Hello World!" );
A list of format specifiers can be seen here https://en.wikipedia.org/wiki/Printf_format_string
Why does it compile?
Because variadic arguments accepted by printf are processed at run time. Not all compilers do compile time checks for validating arguments against the format string. Even if they do they would at most throw a warning, but still compile the program.
It's using the string "%s" as a format string, and using uninitialized memory as the "data".
The only reason it does "something" is because the compiler was apparently not smart enough to recognize that the format string required one parameter and zero parameters were supplied. Or because compiler warnings were ignored and/or errors were turned off.
Just an FYI for anybody who bumps into this: "Always leave all warnings and errors enabled and fix your code until they're gone" This doesn't guarantee correct behaviour but does make "mysterious" problems less likely.
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)));
Can anyone please explain what will be the output and how? I am unable to understand the printf() function arguments. I want to know the difference between
1 and 2, and 3 and 4 printf() statements. Normally in printf(), we should give control string as first argument. But even though interchanging arguments, will I get the same output?
#include <stdio.h>
int main()
{
char *str;
str = "%s";
printf("%s\n", str); //.....1
printf(str, "%s\n"); //.......2
printf(str, "K\n"); //.......3
printf("K\n", str); //........4
return 0;
}
It is better to put the code into a compiler and look at the output. It will probably give better insight of things than asking in Stack Overflow.
The expected output is:
[%s\n] because str contains %s and it will be printed as is.
[%s\n] because str contains the format and the second argument will be printed. This form is very dangerous if str comes from the user.
[K\n] same as 2.
[K\n] str is ignored. gcc will warn you if configured correctly through command-line arguments.
Conclusion — always use #1.
The printf function arguments are comprised of
(1) an initial shift state
(2) format string
(3) list of arguments
In your case you have:
printf("%s\n",str); //.....1
No shift, a format string that takes the sting str = %s and prints it literally.
printf(str, "%s\n");//.......2
The values of str has no numeric shift (again 0) and a literal %s which is printed.
printf(str, "K\n"); //.......3
Here again you have a value of str and initial shift 0, but include a literal format string K which is printed.
printf("K\n",str); //........4
Finally, you have a format string with an insufficient number of conversion specifiers which generates the warning:
foo.c:11:1: warning: too many arguments for format [-Wformat-extra-args]
printf("K\n",str); //........4
but which contains the literal format string K which again is printed. In sum the output of the code being:
%s
%s
K
K
This question already has an answer here:
Code for printf function in C [duplicate]
(1 answer)
Closed 8 years ago.
What is the exact use of % in scanf and printf? And would scanf and printf work without the % sign? All I could find is that % is the conversion specifier but I want to know how it works actually?
% is simply the symbol used to identify the beginning of a conversion specifier in the format string; why % as opposed to any other symbol is an open question, and probably doesn't have that interesting an answer. The printf and scanf functions search the format string for conversion specifiers to tell them the number and types of additional arguments to expect, and how to format the output (for printf) or interpret the input (for scanf).
To print a literal %, you need to use %%.
printf can work without using a conversion specifier, but you'll be limited to writing literal strings. scanf is pretty useless without it.
The '%' character is used in the format string of the scanf() and print()-like functions from the standard header <stdio.h>, to indicate place holders for variable data of several kinds. For example, the format specifier "%d" is a place holder for a value having type int.
Thus, the variadic function printf() expects additional parameters passed as arguments to the function, the first of them having type int.
The value of this int argument is converted to string, and it will be replace to the place holder "%d".
In the case of scanf(), the situation is similar, but now scanf() is an input function that expects that the user enters in command-line a value fitting on the type indicated by the format specifier. Thus, a format specifier "%d" will expect that the user enters a value of type int.
Since all the arguments in C are passed by value, the input data requires you use the address of the variable, to mimic a by-reference mode of passing arguments.
There are a lot of options and details related to these format specifiers.
Yo need to take a look at the bibliography.
For example, start in Wikipedia:
printf() format string
scanf() format string
The % in a scanf() or printf() is a keyword whose purpose is identify the type of data that will be stored in the named variable. So, in the following example, the compiler would build instructions to accept input data of type integer and store it in the memory location at the address assigned to num1:
int num1;
scanf("%d",&num1);
I thought I understand printf, but I guess not. I have:
char sTemp[100];
sprintf(sTemp, "%%%02x", (unsigned)c);
I think that c is an unsigned char and I think a linefeed, but for some reason, what I get coming out is
0x0.000000000000ap-1022
If I make the 'x' in the format string an 'X', then an 'X' appears in the output string.
I completely misinterpreted the results of my experiments in the first version of this answer; apologies all around.
The result of that sprintf() call when c is '\n' is this string:
"%0a"
I believe you are then doing:
printf(sTemp);
Which is the same as:
printf("%0a");
Which is a valid format string for hexadecimal float output. You aren't passing a float variable, however, so printf() pulls whatever happens to be on the stack nearby and uses that as the value to format.
Instead, do:
printf( "%s", sTemp );
and you should see your expected "%0a".
Note that clang, and probably other compilers, give you a warning when you use printf(sTemp):
so.c:9:12: warning: format string is not a string literal (potentially
insecure) [-Wformat-security]
Because of precisely this sort of thing: memory on the stack is accessed that wasn't supposed to be.
i am learning now c and i come up with this example, where i can print a text using pointers.
#include <stdio.h>
main ()
{
char *quotes = "One good thing about music, when it hits you, you feel no pain. \"Bob Marley\"\n";
printf(quotes);
}
I get a warning from the compiler "format not a string literal and no format arguments" and when I execute the program it runs successfully.I read some other questions here that they had the same warning from the compiler but I didn't find an answer that fits me. I understood the reason why i get this message:
This warning is gcc's way of telling you that it cannot verify the format string argument to the printf style function (printf, fprintf... etc). This warning is generated when the compiler can't manually peek into the string and ensure that everything will go as you intend during runtime...
Case 3. Now this is somewhat your case. You are taking a string generated at runtime and trying to print it. The warning you are getting is the compiler warning you that there could be a format specifier in the string. Say for eg "bad%sdata". In this case, the runtime will try to access a non-existent argument to match the %s. Even worse, this could be a user trying to exploit your program (causing it to read data that is not safe to read).
(See the answer)
but what i have to add in my case to in order to have not warnings from the compiler?
Change it to printf("%s", quotes); which adds the specifier that quotes is a 'string', or array of char.
You need to tell printf what is it that you are printing. %s descriptor will tell printf that you are printing a string.
Format of printf = ("descriptor of what type of data you are printing",variable holding the data);
descriptor for strings is %s, for characters %c, for int %d
Change printf to:
printf("%s",quotes);
You have to specify format string - in simplest form:
char *quotes = "One good thing about music(...)\n";
printf("%s", quotes);
or, you can use format string to decorate output:
char *quotes = "One good thing about music(...)"; // no newline
printf("%s\n", quotes); // newline added here
or, if you don't want to mess with format strings:
char *quotes = "One good thing about music(...)"; // no newline
puts(quotes); // puts() adds newline
or
char *quotes = "One good thing about music(...)\n";
fputs(quotes,stdout);
This warning is gcc's way of telling you that it cannot verify the format string argument to the printf style function (printf, fprintf... etc). This warning is generated when the compiler can't manually peek into the string and ensure that everything will go as you intend during runtime. Lets look at a couple of examples.
So as other suggested explicitly use format specifier to tell the compiler...i.e.
printf("%s",quotes);
You are getting the warning because it is dangerous when the string you are printing contains '%'. In this line it makes no sense for percents but when you want to print this for instance:
int main ()
{
int percent = 10;
char *s = "%discount: %d\n";
printf(s, percent);
return 0;
}
your program will likely crash when printf encounters the second percent and it tries to pop a value from the stack from printf.
When you want to print a percent sign use: "%%discount:"
Try this:
#include <stdio.h>
main ()
{
char *quotes = "One good thing about music, when it hits you, you feel no pain. \"Bob Marley\"\n";
puts(quotes); //Either
printf("%s",quotes);//or
return 0;
}