I'm writing a program in C that displays a menu that asks the user to make a decision.
I want to bring up the menu multiple times in the program so I put it into function.
Each choice has a number associated with it:
(1) Add
(2) Subtract
The function displays the menu and then then I scanf the int response to a local variable and then return the variable (I of course declare the variable at the beginning of the function).
int function ()
{
int choice;
// *insert print menu code here*
scanf(" %i", &choice);
return choice;
}
Can I make this shorter by somehow doing a: return scanf(" %i", stdin);
I'm not sure if stdin would be the right choice, but that's what searching tells me.
Sure you can:
int scanInt(void) {
int result;
return (scanf(" %i", &result), result);
}
But is it useful? Probably not.
Note that this might look like undefined behaviour at first glance, because you're writing and reading from the same memory location in one expression. But it is not, as the comma operator introduces a sequence point.
You can't do this with scanf: you'd be returning the return value of scanf, which only indicates the number of items successfully read. If you need to return choice then you'll need to call scanf to fill choice and return it in two separate steps.
Another point here is that you should check the return value of scanf: it will fail if it's unable to convert the input. In your code, scanf will fail if the input is not an integer.
Furthermore, scanf reads from stdin - standard input - by default, you can't pass that as an argument unless you're using fscanf, which otherwise behaves in the same way.
Have you looked at using fgets for your input? It does return the next input line, which would allow you to do the read in the return statement since fgets returns the next line of input as a string. But you'd still need to parse the input yourself, which should probably happen within your function. In any case, fgets is typically a better choice for user input because it separates reading the input from parsing it, and avoids problems when using scanf due to input that doesn't match your format string.
You can, sort of -- but don't.
scanf returns the number of items scanned. You can't make it return the value of one of the scanned items.
return scanf(" %i", &choice), choice;
This uses the comma operator (not to be confused with the comma delimiter between function arguments), which evaluates both operands and yields the result of the right operand.
But there is no good reason to do this. There is no great virtue in making your source code more compact like this. The multi-line form is clearer, and it's easier to modify by adding error handling (what happens if the user enters something other than a number?)
Or you could write your own function that calls scanf and returns the value you want, but that still doesn't address the issue of error handling.
And if your goal is simply to put them on one line:
scanf(" %i", &choice); return choice;
But again, this is not an improvement.
No, you can't with scanf. The function needs a memory location to store the result, and doesn't return it. So you just have to live with the extra code.
Related
So I have this piece of code which is written for reading the user input which is supposed to be between the numbers 1-9. In the code there is and int "choice" declared. But instead of directly scanning the user input using scanf("%d", choice); the programmer has used a char buffer and scanned the buffer instead and then used the atoi function to convert the char input to an integer. I'm confused why has it been done like that instead of directly doing it the easy way. My assumption is that the programmer has done this so that if the user enters a character instead of a number, the code doesn't malfunction. But if that's the case then how would the atoi convert an alphabet to an integer? Here's the code:
int readMenuChoice() {
while (1) {
char buffer[50];
size_t buffLen = 10;
int choice;
showMenu(); //another function that displays all options from 1 to 9
printf("Choose a menu option: ");
scanf("%[^\n]", buffer);
getchar();
choice = atoi(buffer);
if (choice > 0 && choice < 9) {
return choice;
}
printf("Invalid input\n\n");
}
}
We can only guess intention from the coder.
But a pretty likely reason is to make sure that the input stream is empty between each input. I personally do things like that too. But I would have done like this instead:
while (1) {
char buffer[50];
int choice;
showMenu();
printf("Choose a menu option: ");
if(!fgets(buffer, sizeof buffer, stdin)) {
/* Handle error */
}
if(sscanf(buffer, "%d", &choice) != 1) {
/* Handle error */
}
if (choice > 0 && choice < 9) {
return choice;
}
printf("Invalid input\n\n");
}
atoi is an unsafe function. If the argument cannot be parsed to a number, it invokes undefined behavior. And since x = atoi(s) is completely equivalent to sscanf(s, "%d", &x), there's no reason to use the unsafe function. sscanf returns the number of successful assignments, so it CAN be error checked.
The problem of reading user input safely, limiting the input to a certain set of "allowed" inputs, while cleanly disregarding "disallowed" inputs, can be a surprisingly tricky one.
Also surprising, perhaps, is how poor a function scanf is for performing this task, and how difficult it is to solve the problem completely using any algorithm built around scanf.
You asked why this code didn't "directly do it the easy way". By "the easy way" I assume you mean something like
scanf("%d", &choice);
The problem here is that, yes, it can be remarkably difficult to proceed correctly if the user types some non-numeric input.
There are two general avenues to take when trying to handle the possibility that the user types something wrong:
Continue to call scanf("%d") to read the input, but then, try to patch things up if scanf fails. (Obviously the first step here is to check scanf's return value.)
Read a line of input, as text, using something other than scanf. Then attempt to validate that line, and convert it to the desired form.
In my opinion, there is only one choice here, and it is #2. This answer will become far too long if I discuss all the reasons, but the bottom line is that approach #1 is futile. The scanf function has one virtue and one virtue only, and that is that a call like scanf("%d", &choice) is indeed very simple. But the error handling is almost useless. By the time you've built up a reasonable amount of error handling around it, the amount of work you'll have had to do is about three times as much as for approach #2, and you still won't have fully satisfactory results.
So most experienced C programmers will agree that #2 is the only viable approach in the long run. There's a central question advising on good ways of doing input using something other than scanf.
The problem with the code you've posted, IMO, is that it manages to combine the worst of both worlds. It does try to read a line of input as text, then process it later, but the way it reads that line of input is with... the dreaded scanf! And despite trying to be careful in several other ways, this code doesn't even check scanf's return value, so there are some classic problems (like premature EOF) that this code is still vulnerable to.
This code also contains a mysterious extra call to getchar, which is typical of scanf-using code, since stray newlines are almost always a problem.
This code also uses %[...], which is my least favorite scanf format. As I said, scanf's only virtue is simplicity, but a locution like "%[^\n]" is anything but simple. Yes, I know what it does, but IMO it completely defeats the purpose of using scanf for dirt-simple (if less than robust) user input.
But, yes, the primary intent of writing the code this way is probably "so that if the user enters a character instead of a number, the code doesn't malfunction". The code reads a line of text, as text, then attempts to convert the text to a number. You asked what the atoi function does with alphabetic input, and the answer is that (most of the time, anyway) it quietly returns 0. Since 0 isn't a valid input, this code will reject it, so in that sense it works.
To improve this function, the first thing to do would be to replace the calls to scanf and getchar with fgets. The next thing to do would be to replace atoi with strtol. And then it wouldn't be too bad.
I can't seem to find where the problem is in here.
During the first loop, I can enter the name of the creditor but I can't do that during the second loop.
int main(){
float cb,ir,si,sum=0,totaldebt;
int time,i;
char name[25];
printf("------------Welcome to Debt Management System-------------");
for (i=1;i>=1;i++){
printf("\n%d)Name of the creditor: ",i);
scanf("%[^\n]",&name);
printf("Enter your current balance: ");
scanf("%f",&cb);
printf("Enter its interest rate: ");
scanf("%f",&ir);
printf("Enter time for the loan: ");
scanf("%d",&time);
si=cb*ir*time/100;//simple interest
totaldebt=si+cb; //simple interest + current balance
if (name=='none'){
break;
}
sum+=totaldebt;
}
It skip the scanf part and I somewhat guess that the reading part seems stuck by it's previous reading.
To get the effect you seem to be after, %[^\n]*c should be %[^\n]%*c. You need a specifier for the first pattern and then a specifier for a single character. The way you have it written now has you asking scanf to match everything up to a newline, and then read the sequence *c.
It can't read that sequence, but it matches the first specifier. So you end up with an unconsumed newline that is probably tripping your other input.
There's also the potential problem of your for (i=1;i>=1;i++), that condition is fishy, and likely to go on for a while.
Now, while this is all probably a fun exercise, I suggest you ditch scanf and switch to fgets to read lines of input. It's less cryptic, and it forces you to pass a buffer size, which makes using it somewhat easier and safer compared to scanf.
To complete the first answer, I noticed this problem:
if (name=='none')
{
break;
}
This form is not correct in C, you should:
use strcmp
function,
use " instead of ' to define a string:
/* if two strings are the same --but not necessary at the same adress-- strcmp return 0*/
if (0 == strcmp(name, "none"))
{
break;
}
While taking a string input you are not required to use the reference operator '&'. That's why in your first scanf statement use name instead of &name. Also modify the input statement with %*c if you want to take newline character in the input as shown below. Other errors have been stated in the above answers.
scanf("%[^\n]",name); OR
scanf("%[^\n]%*c",name);
Hope you guys can help me with a problem I'm having.
So I have this C code snippet:
int i = 0;
int q = 0;
scanf("%d %d", &i, &q);
When the user enters 4 5, the values 4 and 5 are stored in i and q respectively.
But when the user enters 99.99, 99 is stored in i, but the other 99 after the point goes missing. I do know how scanf works and I understand that scanf will stop scanning after ., but where does the second value go to?
Even if I do:
int i = 0;
int q = 0;
int k = 0;
scanf("%d %d", &i, &q);
scanf("%d", &k);
I still cant get the second value. Where does the second value go and how can I get store it in my variable?
It is trying to look for integer but it is not integer that is left in stdin so it fails.
You can get desired behavior if you do this
int a,b;
scanf("%d.%d",&a,&b);
printf("%d,%d",a,b);
This prints 99,99 as expected.
For your information,
scanf stops at the first mismatch and leaves the rest of the target objects untouched.
Also the unmatched data is left in the input buffer, available for a subsequent read using scanf etc.
Also you can check the return value to determine how many "items" scanf matched.
scanf() doesn't ignore the . while expects to read an int. I suggest you use fgets() and then try to parse with sscanf() which is a better approach. Something like:
#include <stdio.h>
int main(void)
{
int i = 0;
int q = 0;
char line[256];
if (fgets(line, sizeof line, stdin)) {
if (sscanf(line, "%d.%d", &i, &q) == 2) {
printf("i=%d, j=%d\n", i, q);
} else {
printf("Coudn't scan 2 ints\n");
}
} else {
printf("Couldn't read a line\n");
}
}
Note that fgets() will also read in the newline character if there's space in the buffer. It doesn't matter in this case.
But you need to be aware of it and may be undesirable in some cases (e.g., reading a name).
Also see: Why does everyone say not to use scanf? What should I use instead?
Let X be the following statement...
I do know how scanf works
Let Y be the following statement...
... but where does the second value go to?
X and Y do not correspond. Either you know about the return value of scanf, or you don't know how scanf works. That's the bottom line.
... but where does the second value go to?
According to the fscanf manual,
The fscanf() functions shall execute each directive of the format in turn. If a directive fails, as detailed below, the function shall return. Failures are described as input failures (due to the unavailability of input bytes) or matching failures (due to inappropriate input).
The space directive won't match the . input, which will cause a matching failure, scanf will return, meaning that nothing beyond the . byte will be consumed from stdin. In fact, scanf will even put the . back into stdin for you, just before it returns... Isn't that nice?
I gather you understand this (because you said "I do know how scanf works"), yet you seem to be asking about that second value which couldn't have been read due to a previous matching failure... thus, the answer to your first question is, the second value doesn't exist. It can't exist yet, because an input format error exists prior to it.
While we're on the topic of scanf, return values and matching failures, for the remaining question I'm going to assume your code actually looks something more like this:
int i = 0;
int q = 0;
if (scanf("%d %d", &i, &q) != 2) {
puts("Discarding invalid input");
scanf("%*[^\n]"); // discard up to the next newline and...
getchar(); // discard that newline, too.
// XXX: You might want to `return` or `continue` or something
}
... and how can I get store it in my variable?
As mentioned earlier, the space directive doesn't match the period. You need to replace that with something which manipulates the input stream as you intend... That's difficult for me to answer, because I don't have that information, but here's what I'd suggest: Split the two conversions up into two separate calls to scanf. This should give you a nice place in between the two calls to scanf to insert your period-handling code.
Here's a before & after, side-by-side, of the idiom I've adopted for that kind of input:
// BEFORE // AFTER
if (scanf("%d %d", &i, &q) != 2) { if (scanf("%d", &i) != 1) {
// ... puts("Invalid first input...");
// XXX: Handle this???
// XXX: `return` or `continue` or something
}
// XXX: Read a character, check if it's a space (or a dot, or something else which you have in your mind and I don't have in mine)
if (scanf("%d", &q) != 1) {
// ...
Handling the erroneous period could be as simple as exiting the process or calling getchar() to discard a single character, then continueing in order to try again. These solutions won't solve all user input errors, and might even confuse or frustrate some users. I recommend sticking with the discard the rest of the line method, at least until you learn about all of the alternatives for user input and what they're each best at. After all, people tend to avoid using software which confuses them...
... hence the reason people tend to discourage scanf... because I guess there's no such thing as literature which sets a standard, and thus there's no way to correct my scanf-related misunderstandings.
I am learning binary search tree. Given below is the main function of a program to perform basic BST operations. The option variable chooses which operation to perform for switch
int main()
{
struct node* tree=NULL;
struct node* ptr=NULL;
struct node* ptrm=NULL;
int val;
int option;
do
{
printf("\n1.Insert Node\n2.Preorder Traversal\n3.Postorder Traversal\n4.Inorder Traversal\n5.find_smallest_element\n6.find_largest_element\n7.Delete Element\n8.Total_nodes\n9.total_external_nodes\n10.total_internal_nodes\n11.Mirror image\n12.Exit\n");
printf("\nEnter option");
scanf("%d",&option);
switch(option)
{
case 1:
printf("\nEnter value to be inserted");
scanf("%d",&val);
tree=insert_element(&tree,val);
printf("\n%d Inserted\n",val);
break;
case 2:
preorder(&tree);
break;
case 3:
postorder(&tree);
break;
case 4:
inorder(&tree);
break;
case 5:
ptr=find_smallest_element(&tree);
printf("\nSmallest element:%d",ptr->data);
break;
case 6:
ptr=find_largest_element(&tree);
printf("\nLargest element:%d",ptr->data);
break;
case 7:
printf("\nEnter value of element to be deleted");
scanf("%d",&val);
tree=delete_node(&tree,val);
break;
case 8:
printf("\nTotal nodes%d",total_nodes(&tree));
break;
case 9:
printf("\nTotal External nodes%d",total_external_nodes(&tree));
break;
case 10:
printf("\nTotal Internal nodes%d",total_internal_nodes(&tree));
break;
case 11:
ptrm=mirror_image(&tree);
}
}while(option!=12);
return 0;
Everything works fine when i give int data as input for 'option'.However, when i give a char input the program goes into infinite loop and displays option list repeatedly.
Why does this happen?
Since you used %d format specifier in the scanf() format string,
scanf("%d",&val);
will successfully assign to val only if an integer was given as the input. If a char is given instead, scanf() (which returns the number of successful assignments) will return 0 here and will leave the char in the input buffer unconsumed.
During the next iteration of the loop, this char would still be in the input buffer and scanf() would end up trying to read the same thing and will won't assign to val once again.
This will go on and on resulting in an infinite loop.
To solve this, check the value returned by scanf(). If it is not 1, clear the input buffer till the next \n (newline) like
int t;
while( (t=getchar()) != `\n` );
This will consume the old data till a \n from the input buffer.
You could then use the continue statement to skip the rest of that iteration of the loop.
Read about getchar() here.
Why does this happen?
The roots of this issue stem all the way back to how scanf indicates error codes to your code (not at all, because your code discards them), and what scanf("%d", &val) is expected to do when non-decimal input is encountered; it stops reading input, possibly returning an error code, but your code discards that and continues on merrily trying to delete the node indicated by the value which may not have been read, leading to possible use of an uninitialised variable later...
Some people take the guessing to an extreme, and think it's appropriate to use fflush(stdin) to solve this (it isn't; don't do that...). You've not gone that far, but I think it might be a good idea to start reading the manuals of the functions you're using. The scanf manual is here. Make note of that URL, and realise that you can look up other standard functions (both C99 and POSIX standard) by substituting the name of the function.
The first thing your code must do is check that return value, which your manual will document in the RETURN VALUES section; as with most standard library functions, scanf has a return value which your code should most likely contain critical logic regarding! From there, how you handle errors is your business. Perhaps it might be appropriate to use something simple yet user-unfriendly, like:
perror(scanf);
exit(EXIT_FAILURE);
You should seek the simpler solutions where possible, to avoid overcomplicating things. If your input doesn't come directly from the user, or you just want to prototype, you should use the solution above. You can always change exit(EXIT_FAILURE) to return EXIT_FAILURE; and return 0; on success, if necessary later.
If you choose to keep your program running, how much of the user input gets discarded due to the typo is up to you. By far the simplest option is to just read a single character (using getchar();)...
You could choose to discard a word of input, like so: scanf("%*s");. The * informs scanf to read and discard the input, rather than reading and assigning.
Neither of those options strike me as being particularly user-friendly. If you're going to the effort of making a user-friendly interface, you'll probably want to choose one of the following options.
Using the * assignment-suppression modifier, you can also discard a line of input, like so:
scanf("%*[^\n]");
getchar();
The getchar(); is necessary to discard the newline character, which we expect to be discarded when a line is discarded.
Using the command line arguments for your input, rather than using stdin (or other files/streams). Some surprisingly simple yet versatile menus have been produced this way, such as the ones your compiler presents to you. Your mode of input then changes to using friendlier functions such as sscanf, and developing your program not as a looping program that remains open, but as an instant program which gets executed every now and then, when necessary, to update records or what-not.
Using the graphical user interface instead of the console. Well, that one really makes the ol' noggin' flog, eh? You could use... a context menu such as the File/Edit/etc menus in Windows, or a listbox (which would be more touch-screen friendly) to prompt your user for a selection.
Suffice to say, this looks like homework, so you probably don't have the choice to design a more appropriate user interface... In this case, I suggest using the * assignment-suppression modifier as per above (the first bolded section).
Apologizes I am very inexperienced with C. I have the following code:
char * a[BUF_SIZE];
scanf("%d", numberOf);
do {
a[i] = (char *)malloc(MAX_LINE_LEN + 1);
scanf("%s", a[i]);
++i;
} while(i < numberOf);
The idea is simple, read two inputs from stdin using scanf, the first being a single int, followed by some array of strings. Scanf works independently in both cases e.g. scanf("%d", numberOf) will store a digit and scanf("%s", a[i]) will store a set of strings into the array. However in conjunction reading an integer first into numberOf causes a segfault when reading in a set of strings. My question is why? I know its generally bad practice to use scanf, but I fail to see how reading in multiple inputs from stdin can cause a segfault in the resulting code. Much Thanks!
From the code, it looks like numberOf is an int. When using scanf, you want to pass it a pointer, so change scanf("%d", numberOf); to scanf("%d", &numberOf);
What scanf does is take the user input and put it into the memory address specified by the second parameter. When you provide an int as the second parameter, scanf tries to put user input into a memory address (specified by the int) that it may not own, causing the seg-fault.
You are missing & operator in scanf("%d", numberOf); put it as &numberOf