What are scanf("%*s") and scanf("%*d") format identifiers? - c

What is the practical use of the formats "%*" in scanf(). If this format exists, there has to be some purpose behind it. The following program gives weird output.
#include<stdio.h>
int main()
{
int i;
char str[1024];
printf("Enter text: ");
scanf("%*s", &str);
printf("%s\n", str);
printf("Enter interger: ");
scanf("%*d", &i);
printf("%d\n", i);
return 0;
}
Output:
manav#workstation:~$ gcc -Wall -pedantic d.c
d.c: In function ‘main’:
d.c:8: warning: too many arguments for format
d.c:12: warning: too many arguments for format
manav#manav-workstation:~$ ./a.out
Enter text: manav
D
Enter interger: 12345
372
manav#workstation:~$

For printf, the * allows you to specify the minimum field width through an extra parameter, e.g. printf("%*d", 4, 100); specifies a field width of 4. A field width of 4 means that if a number takes less than 4 characters to print, space characters are printed until the field width is filled. If the number takes up more space than the specified field width, the number is printed as-is with no truncation.
For scanf, the * indicates that the field is to be read but ignored, so that e.g. scanf("%*d %d", &i) for the input "12 34" will ignore 12 and read 34 into the integer i.

The star is a flag character, which says to ignore the text read by the specification.
To qoute from the glibc documentation:
An optional flag character `*', which says to ignore the text read for this specification. When scanf finds a conversion specification that uses this flag, it reads input as directed by the rest of the conversion specification, but it discards this input, does not use a pointer argument, and does not increment the count of successful assignments.
It is useful in situations when the specification string contains more than one element, eg.:
scanf("%d %*s %d", &i, &j) for the "12 test 34" - where i & j are integers and you wish to ignore the rest.

The * is used to skip an input without putting it in any variable. So scanf("%*d %d", &i); would read two integers and put the second one in i.
The value that was output in your code is just the value that was in the uninitialized i variable - the scanf call didn't change it.

See here
An optional starting asterisk indicates that the data is to be retrieved from stdin but ignored, i.e. it is not stored in the corresponding argument.

In scanf("%*d",&a) * skips the input. In order to read the inputs one has to use an extra "%d" in scanf. For example:
int a=1,b=2,c=3;
scanf("%d %*d %d",&a,&b,&c); //input is given as: 10 20 30
O/p:
a=10 b=30 and c=3; // 20 is skipped
If you use another %d i.e: scanf("%d %*d %d %d",&a,&b,&c); //input is given as: 10 20 30 40
then a=10 b=30 c=40.
If you use "," in scanf then no value will be taken after %*d i.e;
scanf("%d %*d,%d" &a,&b,&c)// 10 20 30
O/p:
a=10 b=2 c=3 will be the output.

Related

Why is scanf failing to read inputs correctly?

I cant figure out whats wrong. Am i using format specifiers in wrong way? Someone please help i am very new to coding.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[20];int age;char grade;double gpa;char area[10];
printf("User Input\n");
printf("Enter your name: ");
fgets(name,20,stdin);
printf("Your name is %s\n",name);
printf("Enter your age: ");
scanf("%d\n",&age);
printf("Your age is %d\n",age);
printf("Enter you grade: ");
scanf("%c\n",&grade);
printf("Your grade is %c\n",grade);//Why is this giving an int output?
printf("Enter your gpa: ");
scanf("%f\n",&gpa);
printf("Your gpa is %f\n",gpa);
printf("Enter your area: ");
scanf("%s\n",&area);
printf("Your area is %s",area);//This shows grade input
return 0;
}
Output
You use fgets correctly when reading name. I'd recommend also using fgets for all your other inputs, and then parsing the intended values out of them. For example:
char age_str[20];
fgets(age_str, 20, stdin);
age = strtol(age_str, NULL, 10);
This is preferable to using scanf directly for non-string inputs since if input fails to match a format string, it will remain in stdin and screw up the other scanf calls.
If you would like to use scanf correctly:
Check its return value to see if it matches the number of format specifiers in the string. If not, some inputs were not successfully read. You may want to use a do/while loop for this.
Begin your format strings with a space, as in " %c", so that any whitespace remaining in stdin will be skipped over.
Don't end your format strings with a newline.
Some things to remember about scanf:
Most conversion specifiers like %s, %d, and %f will skip over leading whitespace - %c and %[ will not. If you want to read the next single non-whitespace character, use " %c" - the leading blank tells scanf skip over any leading whitespace before reading the next non-whitespace character;
For what you are trying to do, you should not use \n in your format strings - it will cause scanf to block until you enter a non-whitespace character;
You do not need to use the & operator on array expressions like area; under most circumstances, array expressions are converted to pointer expressions1. Honestly, you should read area the same way you read name, using fgets (and you should always check the result of fgets), or you should specify the maximum field width in the specifier: scanf( "%9s", area ); (a 10-element array can hold up to a 9-character string, since one element has to be reserved for the string terminator);
You should get in the habit of checking the result of scanf - it will return the number of successful conversions and assignments. For example, scanf( "%d %d", &x, &y ) will return 2 if both x and y are read successfully. It will return EOF if end-of-file is signaled or there's a read error.
scanf will read up to the next character that doesn't match the conversion specifier - IOW, if you're using %d, then scanf will skip over any leading whitespace, then read up to the next character that isn't a decimal digit. That character is left in the input stream. This means if you're using %d and type in 123e456, scanf will read up to that 'e' character and assign 123 to the target. If you try to read again with %d, scanf will immediately stop reading on that e and return 0 without assigning anything to the target (this is called a matching failure). This will continue until you remove that 'e' from the input stream (such as with getchar or fgetc or scanf with the %c specifier, etc.
You need to make sure the types of the arguments match the format specifier. %s expects an argument of type char *, %d expects int *, %f expects float *. %x expects unsigned int *, %lf expects double *, etc.
This is one of the "deeply unintuitive" aspects of C I was talking about in my comment.

The practical difference of %c vs %s

I'm required to do a seafood menu as an assignment in my college. I will provide the relevant code here:
int main ()
{
int a,d;
float c ;
char b,s,S,m,M,l,L;
printf ("\n\t\t\t\tSeafood Menu\n");
printf ("----------------------------------------------------------------------------------\n");
printf ("\t\t\t\t\t\t Dish Size\n");
printf ("Item Number\t Seafood Dish\t Small\t Medium Large\t\n");
printf ("----------------------------------------------------------------------------------\n");
printf ("\t 1\t Fried \t20.00 \t\t 40.00 \t\t 55.00\n");
//Continue with a bunch of menu here
printf (" Enter item number :");
scanf ("%d",&a);
printf (" Enter dish size (S/M/L) :");
scanf ("%s",&b);
if ((a==1)&&((b=='s')||(b=='S')))
{c=20.00;}
//continue with a bunch of condition to choose the price per dish stated in the menu
printf (" Enter dish quantity :");
scanf ("%d",&d);
printf (" Price per dish size :RM%.2f\n\n\n",c);
return 0;
}
When I tried to change the format identifier in this to %c, it just stopped accepting input for that particular scanf.
printf ("Enter dish size (S/M/L):");
scanf ("%s",b);
I would like to attach images but it seems that I am not allowed to do so 'll leave two links:
Normal, using %s: and abnormal, using %c
I'm curious about why it doesn't work when I use %c while %s works? As all I enter there is just character. Please enlighten me.
I'm curious about why it doesn't work when I use %c while %s works?
Well, guess what, it did not work, even if it appeared to.
As mentioned in the man page, for scanf(), the type of arguments are
%c expects a pointer to char
%s expects a pointer to the initial element of an char array.
Quoting C11, chapter §7.21.6.2, (emphasis mine)
c Matches a sequence of characters of exactly the number specified by the field
width (1 if no field width is present in the directive).
If no l length modifier is present, the corresponding argument shall be a
pointer to the initial element of a character array large enough to accept the
sequence. No null character is added.
s Matches a sequence of non-white-space characters.
If no l length modifier is present,the corresponding argument shall be a
pointer to the initial element of a character array large enough to accept the
sequence and a terminating null character, which will be added automatically.
In your case,
providing %s is wrong, as a single char is one element too short to scan and hold an array, that is null-terminated. This causes undefined behavior as memory overrun happens.
%c should have "worked" , if the input stream did not have the newline stored from previous ENTER key press. If you clean off the input stream of all pending inputs, you'll see it works. Use something like
scanf(" %c", &b); //the whitespace "eats up" all the previous whitespaces
You can see this previous answer of mine, for details on the same.

having trouble with a "\n" and scanf

Here is the code
printf("\n");
printf("Enter a integer vaule:");
scanf("%d" , &num3);
printf("You entered: %015d", num3);
printf("Enter a float value:");
scanf("%f", &deci3);
printf("You entered: %15.2f", deci3);
printf("\n");
the output is
Enter a integer vaule:4.4
You entered: 000000000000004
Enter a float value:You entered: 0.40
The problem is this code is not stopping at
printf("Enter a float value:");
and this scanf
scanf("%f", &deci3);
seems to be getting its value from the previous scanf
The %d conversion stops wherever the integer stops, which is a decimal point. If you want to discard the input there, do so explicitly… getc in a loop, fgets, or such. This also allows you to validate the input. The program should probably complain about 4.4.
The scanf function works this way per the specification:
An input item shall be defined as the longest sequence of input bytes (up to any specified maximum field width, which may be measured in characters or bytes dependent on the conversion specifier) which is an initial subsequence of a matching sequence. [Emphasis added.]
In your example, the following C string represents the contents of stdin when the first scanf call requests input: "4.4\n".
For this initial call, your format string consists of a single specifier, %d, which represents an integer. That means that the function will read as many bytes as possible from stdin which satisfy the definition of an integer. In your example, that's just 4, leaving stdin to contain ".4\n" (if this is confusing for you, you might want to check out what an integer is).
The second call to scanf does not request any additional input from the user because stdin already contains ".4\n" as shown above. Using the format string %f attempts to read a floating-point number from the current value of stdin. The number it reads is .4 (per the specification, scanf disregards whitespace like \n in most cases).
To fully answer your question, the problem is not that you're misusing scanf, but rather that there's a mismatch between what you're inputting and how you're expecting scanf to behave.
If you want to guarantee that people can't mess up the input like that, I would recommend using strtol and strtod in conjunction with fgets instead.
This works, but it dont complains if you type 4.4 for the int
#include <stdio.h>
int main() {
char buffer[256];
int i;
float f;
printf("enter an integer : ");
fgets(buffer,256,stdin);
sscanf(buffer, "%d", &i);
printf("you entered : %d\n", i);
printf("enter a float : ");
fgets(buffer,256,stdin);
sscanf(buffer, "%f", &f);
printf("you entered : %f\n", f) ;
return 0;
}
use a fflush(stdin) function after the fist scanf(), this will flush the input buffer.

whitespace in the format string (scanf)

Consider the following code:
#include<stdio.h>
int main() {
int i=3, j=4;
scanf("%d c %d",&i,&j);
printf("%d %d",i,j);
return 0;
}
It works if I give 2c3 or 2 c 3 or 2c 3 as input if I have to change the value of variables. What should I do if I want the user to enter the same pattern as I want means if %dc%d then only 2c3 is acceptable and not 2 c 3 and vice versa if it is %d c %d?
Whitespace in the format string matches 0 or more whitespace characters in the input.
So "%d c %d" expects number, then any amount of whitespace characters, then character c, then any amount of whitespace characters and another number at the end.
"%dc%d" expects number, c, number.
Also note, that if you use * in the format string, it suppresses assignment:
%*c = read 1 character, but don't assign it to any variable
So you can use "%d%*c c%*c %d" if you want to force user to enter: number, at least 1 character followed by any amount of whitespace characters, c, at least 1 character followed by any amount of whitespace characters again and number.
If you want to accept 1c2 but not 1 c 2, use the pattern without the space:
scanf("%dc%d", &x, &y);
If you want to accept 1c2 and 1 c 2 (and also 1 \t \t c \t 2 etc), use the pattern with the space:
scanf("%d c %d", &x, &y);
If you want to accept 1 c 2 but not 1c2, add a fake string containing whitespace:
scanf("%d%*[ \t]c%*[ \t]%d", &x, &y);
Here the format string %[ \t] would mean "read a string that contains any number of space and tab characters"; but using the additional *, it becomes "expect a string that contains any number of space and tab characters; then discard it"
I think I would read the scanf result into different variables (i.e. not reuse i and j) as "%d%s%d". Then check the string you got from the %s and if it matches your requirements, use the other variables to overwrite i and j.
Force a string parsing first :
char a[100], b[100];
scanf("%99s c %99s", a, b);
Then use sscanf() to convert the strings to int.

Why does scanf not terminate when I expect it to

If I write a C program then it will not automatically get out of if else like ....
#include<stdio.h>
int main ()
{
int a, b, c, d;
printf ("enter the value ");
scanf("%d %d %d ",&a,&b,&c);
d=a+b+c;
if(d==180)
printf("triangle is valid ");
else
printf("triangle is invalid ");
return 0;
}
then it will not terminate itself.....
Can anyone help to figure out what the problem in this .....
It's the space at the end of the scanf format string. Remove that space and your program will terminate.
I guess there is inconsistency between the scanf() format string and the format you enter your data in. But seriously, you should accept some old answers before asking new questions.
Omit the spaces in the scanf string
scanf("%d%d%d",&a,&b,&c);
scanf function normally skip the space between the inputs.
In your code you ask the input in the following format
scanf("%d %d %d ",&a,&b,&c);
It is represent the 1input as 1 space,1input and 1 space, 1input and 1 space.
So if you give the three input after the enter, it will skip the new line also.
Because scanf function will take the input as non white space character.
To avoid this you need to give the 4 input. So that time, the first three inputs are stored in the variable a b c, Then next space and values are stored in the buffer.
After run the program you need to give the input like the folllowing
12 12 12 12
Here the first three inputs are stored in the a b c variables.
Otherwise your scanf format should be the following format
scanf("%d%d%d",&a,&b,&c);

Resources