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);
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)));
int currD, currM, currY;
scanf("%d/%d/%d", &currD, &currM, &currY);
I saw this code receiving birth date in the format DD/MM/YYYY, but I wonder what's the meaning of putting '/', I know without this, it will lead to bad input because of the character '/'. So what does it actually mean?
When encountering code that you don’t understand, and which is calling a function from a library, your first order of business is to research the documentation for that function. For C standard functions it’s enough to google the function name.
A good reference in this case is cppreference (don’t be misled by the website name, this is the C reference, not the C++ reference). It gives the function’s definition as
int scanf( const char *format, ... );
Now look for the parameter description of the format parameter:
pointer to a null-terminated character string specifying how to read the input.
The subsequent text explains how to read the format string. In particular:
[…] character [except %] in the format string consumes exactly one identical character from the input stream, or causes the function to fail if the next character on the stream does not compare equal.
conversion specifications [in] the following format
introductory % character
conversion format specifier
d — matches a decimal integer.
In other words:
scanf parses a textual input based on the format string. Inside the format string, / matches a slash in the user input literally. %d matches a decimal integer.
Therefore, scanf("%d/%d/%d", …) will match a string consisting of three integers separated by slashes, and store the number values inside the pointed-to variables.
Is just the separator in the date format. The error must raise when some function searchs for those /.
The first parameter of scanf is a string specifying the format of the string you want to use to store the informations in the further arguments. You can see this format string as a pattern : %d means an integer, and without the '%' it means it just has to match exactly the characters.
Input is expected to provide like 04/07/2019.
If input is provided only 04072019. currD alone hold the value 04072019, currM and currY might garbage value as it is not initialised.
It expects the input to be in the format three integers separated by two slashes ("/"). For example: 10/11/1999.
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.
Code I have:
int main(){
char readChars[3];
puts("Enter the value of the card please:");
scanf(readChars);
printf(readChars);
printf("done");
}
All I see is:
"done"
after I enter some value to terminal and pressing Enter, why?
Edit:
Isn't the prototype for scanf:
int scanf(const char *format, ...);
So I should be able to use it with just one argument?
The actual problem is that you are passing an uninitialized array as the format to scanf().
Also you are invoking scanf() the wrong way try this
if (scanf("%2s", readChars) == 1)
printf("%s\n", readChars);
scanf() as well as printf() use a format string and that's actually the cause for the f in their name.
And yes you are able to use it with just one argument, scanf() scans input according to the format string, the format string uses special values that are matched against the input, if you don't specify at least one then scanf() will only be useful for input validation.
The following was extracted from C11 draft
7.21.6.2 The fscanf function
The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: one or more white-space characters, an ordinary multibyte character (neither % nor a white-space character), or a conversion specification. 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.
as you can read above, you need to pass at least one conversion specifier, and in that case the corresponding argument to store the converted value, if you pass the conversion specifier but you don't give an argument for it, the behavior is undefined.
Yes, it is possible to call scanf with just one parameter, and it may even be useful on occasion. But it wouldn't do what you apparently thought it would. (It would just expect the characters in the argument in the input stream and skip them.) You didn't notice because you failed to do due diligence as a programmer. I'll list what you should do:
RTFM. scanf's first parameter is a format string. Plain characters which are not part of conversion sequences and are not whitespace are expected literally in the input. They are read and discarded. If they do not appear, conversion stops there, and the position in the input stream where the unexpected character occured is the start of subsequent reads. In your case probably no character was ever successfully read from the input, but you don't know for sure, because you didn't initialize the format string (see below).
Another interesting detail is scanf's return value which indicates the number items successfully read. I'll discuss that below together with the importance to check return values.
Initialize locals. C doesn't automatically initialize local data for performance reasons (in today's light one would probably enforce user initialization like other languages do, or make auto initialization a default with an opt-out possibility for the few inner loops where it would hurt). Because you didn't initialize readchars, you don't know what's in it, so you don't know what scanf expected in the input stream. On top it probably is nominally undefined behaviour. (But on your PC it shouldn't do anything unexpected.)
Check return values. scanf probably returned 0 in your example. The manual states that scanf returns the number of items successfully read, here 0, i.e. no input conversion took place. This type of undetected failure can be fatal in long sequences of read operations because the following scanfs may read in one-off indexes from a sequence of tokens, or may stall as well (and not update their pointees at all), etc.
Please bear with me -- I do not always read the manual, check return values or (by error) initialize variables for little test programs. But if it doesn't work, it's part of my investigation. And before I ask anybody, let alone the world, I make damn sure that I have done my best to find out what I did wrong, beforehand.
You're not using scanf correctly:
scanf(formatstring, address_of_destination,...)
is the right way to do it.
EDIT:
Isn't the prototype for scanf:
int scanf(const char *format, ...);
So I should be able to use it with just one argument?
No, you should not. Please read documentation on scanf; format is a string specifying what scanf should read, and the ... are the things that scanf should read into.
The first argument to scanf is the format string. What you need is:
scanf("%2s", readChars);
It Should provided Format specifiers in scanf function
char readChars[3];
puts("Enter the value of the card please:");
scanf("%s",readChars);
printf("%s",readChars);
printf("done");
http://www.cplusplus.com/reference/cstdio/scanf/ more info...
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.