I am having trouble with multiple chars and scanf_s() [duplicate] - c

This question already has answers here:
Reading a character with scanf_s
(3 answers)
Closed 3 years ago.
I'm trying to use scanf_s() to read in multiple values but every time I run the program, I get
Unhandled exception at 0x592AD6AC (msvcr120d.dll) in lab 2.exe: 0xC0000005: Access violation writing location 0x00000000.
in a popup window. How do I fix this?
float inTemp;
char inUnit;
char outUnit;
printf("Please enter the starting temperature with its units and the units\nyou would like to convert to (i.e. 74.5 F C): ");
scanf_s("%f %c %c", &inTemp, &inUnit, &outUnit); //takes in all user input (NOT WORKING)

You have not provided a length indicator. From MSDN:
Unlike scanf and wscanf, scanf_s and wscanf_s require the buffer size
to be specified for all input parameters of type c, C, s, S, or string
control sets that are enclosed in []. The buffer size in characters is
passed as an additional parameter immediately following the pointer to
the buffer or variable. For example, if you are reading a string, the
buffer size for that string is passed as follows: char s[10];
scanf_s("%9s", s, _countof(s)); // buffer size is 10, width
specification is 9
Here is a link to that page.

If you really want to use (less portable) scanf_s, from the C11 standard (n1570), Annex K 3.5.3.2 p.4:
The fscanf_s function is equivalent to fscanf except that the c, s, and [ conversion specifiers apply to a pair of arguments (unless assignment suppression is indicated by a *). The first of these arguments is the same as for fscanf. That argument is immediately followed in the argument list by the second argument, which has type rsize_t and gives the number of elements in the array pointed to by the first argument of the pair.
You need to give the lengths of your char * arguments:
scanf_s("%f %c %c", &inTemp, &inUnit, 1, &outUnit, 1);
Alternatively, just use scanf:
scanf("%f %c %c", &inTemp, &inUnit, &outUnit);
And, as always, check the return values.
In general, as scanf is somtimes useful for little quick-and-dirty programmes, it’s of less use for productive software, because handling errors is hard. Use fgets and sscanf/strtod/strtol/… instead.

Related

C Programming- Scanf not working. Tried adding space, doesnt work [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
int main(int argc, char *argv[]) {
int option;
char user[50];
system("COLOR b");
printf("~~~~~~~~~~~~~~~~ GLENMUIR HIGH SCHOOL LIBRARY MANAGEMENT SYSTEM ~~~~~~~~~~~~~~~~~~~~~~~ \n");
printf("Username:");
scanf("%c",&user);
checkPass();
printf("\n | Options Menu |\n");
printf(" | Check Out Book(1), Return Book(2) , Register(3), Admin Login (4) |\n");
printf("Option:");
scanf("%d",&option); /*compiler ignores this completely*/
I tried adding space before %d, but still didn't work. Any solutions for a newb?
Your problems stem from this statement:
scanf("%c",&user);
The %c conversion specifier only reads a single character from the input stream, not a string. If you entered a username like jbode, only the 'j' character is read from the input stream - the remaining characters will be read by the next scanf (or other input) call.
Unfortunately, that next scanf call is expecting a sequence of decimal digits due to the %d conversion specifier; 'b' is not a decimal digit, so you get a matching failure - option is not updated, 'b' is not removed from the input stream, and scanf returns a 0 to indicate that no successful conversion occurred.
The right answer is to not use scanf to read the username at all, but to use fgets instead:
if ( !fgets( user, sizeof user, stdin ) )
{
// EOF or error detected on input, handle as appropriate
}
checkPass();
...
If you really want to use scanf, though, the right approach would be:
if ( scanf( "%49s", user ) != 1 ) // no & operator on user
{
// EOF or error detected on input, handle as appropriate
}
checkPass();
...
You do not need to use the & operator when reading a string into an array of char. The expression user will implicitly be converted ("decay") from type char [50] to char *, and the resulting pointer value will be the address of the first element of the array.
From standard 7.21.6.2 under the conversion specifiers and their meanings:
s
Matches a sequence of non-white-space characters.286) 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.
Well as an alternative to scanf you can try looking at fgets.
So the answer will be scanf("%s",user); or more correct use would be scanf("%49s",user). To get a more elanorate idea of why you should consider fgets over scanf read through this discussion.
Also there is no need to put space before %d format specifier because as per standard 7.21.6.2
Input white-space characters (as specified by the isspace function)
are skipped, unless the specification includes a [, c, or n specifier.
Apart from the change mentioned above check the return value of scanf.
if( scanf("%49s",user) != 1){
fprintf(stderr,"You have given wrong input - use input just a word\n");
exit(1);
}
In your case if you have given wrong input then this would fail and you will get the error message.
You can use %d specifier in scanf like this, no need to provide ' '.
if( scanf("%d",&option)!= 1){
/* handle error. Put appropriate meaningful error message. */
exit(1);
}
Also in Dev-cpp try to add compiler flags -Wall -Wextra that will show you warnings.
Go to Tools -> Compiler Options
Now in general tab under Adds the following command when compiler is called and -Wall -Wextraand pressOK`,=.
Compile it. You will get some error like this - [Warning] format %c expects argument of type char *, but argument 2 has type char (*)[20] [-Wformat=]`
&user denotes an pointer to an array of chars containing 20 char. But as mentioned above you have to pass char* as second argument to scanf.

Are scanf arguments allowed to alias? [duplicate]

This question already has answers here:
Is scanf("%d%d", &x, &x) well defined?
(3 answers)
Closed 6 years ago.
I'm using scanf to extract very regular tabular data. I need to go over it in two passes and can safely ignore some of the arguments some of the time.
I want to allocate space for only the strings I care about and a single "discard" string for the others. If I pass this argument multiple times as output, is the behavior defined?
A silly minimal example:
char label[MAX_SIZE], discard[MAX_SIZE];
sscanf(input, "%s %s %s %s", discard, label, discard, discard);
I cannot find any language in the C11 standard that makes your intended usage invalid.
There seems to be a better solution, though. If you place a * (“assignment suppression character”) after the % in the format string, then the scanf function will parse but not store the input item. Hence, you don't have to (must not, actually) provide a destination argument for it. Seems to be exactly what you need here.
If I pass this argument multiple times as output, is the behaviour defined?
Citing C11:
7.21.6.2 The fscanf function
int fscanf(FILE * restrict stream,
const char * restrict format, ...);
The fscanf function executes each directive of the format in turn. When all directives have been executed, or if a directive fails (as detailed below), the function returns.
The fscanf function (and therefore sscanf) executes each directive of the format in turn, therefore there should be no problems assigning to discard parameters multiple times, as the final assignment would have the final effect (overwriting the previous assignments).

strcmp always true regardless of input

So I'm currently learning C programming in preparation for the college semester starting up. Kind of like a "leg up" thing. Anyways, I was doing some practice with comparing strings and putting new values in string arrays using the string copy function, but I'm getting some unexpected behavior that I really can't explain. It always shows a positive result for the string compare check no matter what I enter, and I can't identify where I went wrong. If someone would give me assistance I would greatly appreciate it!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char tVar1[10];
char tVar2[10];
printf("Enter your first test variable: \n");
scanf("%c", &tVar1);
strcpy(tVar2, "yes");
if(strcmp(tVar1, "yes") == 0)
{
printf("It would probably be more effective if you used two different variables.");
}
else
{
printf("Nope, just the same amount of effectiveness.");
}
return 0;
}
%c is used to read a single character value as opposed to a string. When you type in your string on the terminal, only the first character is being read and assigned to tVar1[0]. To read a string from the input stream, you need to use the %s conversion specifier:
scanf( "%9s", tVar1 ); // note no & on tVar1
tVar1 is sized to hold 10 elements, which means we can read at most 9 characters into the buffer (one element must be reserved for the string terminator). The 9 is %9s prevents us from reading more characters into the buffer than it can hold.
Unfortunately, the size must be hard-coded as part of the conversion specifier. There's no way to provide the size as a run-time argument (as there is for printf). There are ways around this - you can build the format string dynamically at runtime, for example - but it's a pain regardless.
Notice that we don't use a & for tVar1 in the scanf call. Under most circumstances, an expression of type "N-element array of T" will be converted to an expression of type "pointer to T", and the value of the expression will be the address of the first element.
To intake a string, change
scanf("%c", &tVar1);
to
scanf("%9s", tVar1);
If you want to read an entire input line(not just a word):
#define MAXLINE 9
char line[MAXLINE + 1];
fgets(line, MAXLINE, stdin);
Note that fgets adds a trailing newline, so you'll need to overwrite that with the null byte.
fgets is also better than scanf, because scanf leaves stdin with a newline, which will cause next input calls to be ``skipped".
When you use %c in scanf it will read only one character. To read a word (text until white space) use %s:
scanf("%9s", tVar1);
The 9 limits the number of characters to read so that you avoid the common butter overflow vulnerability.

Why is this program not printing the input I provided? (C)

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...

Header for scanf_s function

While answering this question I compiled the code on Ideone and got this error
implicit declaration of function ‘scanf_s’ [-Wimplicit-function-declaration]
Isn't stdio.h is the header for scanf_s?
scanf_s is Microsoft-specific. Header is stdio.h but not in GCC.
Used to Read formatted data from the standard input stream. These versions of scanf,scanf_s, _scanf_l, wscanf, _wscanf_l have security enhancements
Where as Ideone uses GCC because of this only you got undefined reference to scanf_s
Mostly You can found this function in windows based compilers like
Visual Studio 2008 and Microsoft .NET 2010
Here and Here You found Interesting info about scanf_s
int scanf_s(
const char *format [,
argument]...
);
According to the MSDN help:
The scanf_s function reads data from the standard input stream stdin and writes the data into the location given by argument. Each argument must be a pointer to a variable of a type that corresponds to a type specifier in format. If copying takes place between strings that overlap, the behavior is undefined.
Unlike scanf, scanf_s requires the buffer size to be specified for all input parameters of type c, C, s, S, or [. The buffer size is passed as an additional parameter immediately following the pointer to the buffer or variable. For example, if reading a string, the buffer size for that string is passed as follows:
char s[10];
scanf_s("%9s", s, 10);
The buffer size includes the terminating null. A width specification field may be used to ensure that the token read in will fit into the buffer. If no width specification field is used, and the token read is too big to fit in the buffer, nothing will be written to that buffer.
In the case of characters, one may read a single character as follows:
char c;
scanf_s("%c", &c, 1);
When reading multiple characters for non-null terminated strings, integers are used as the width specification and the buffer size.
char c[4];
scanf_s("%4c", &c, 4); // not null terminated

Resources