do-while or scanf() in c does not work - c

On running this code, the outer do-while does not work. It prints
"Do you want to enter another number(y/n)-"
but it does not let me enter y or n in order to use the program again. Where did I go wrong?
#include <stdio.h>
#include <math.h>
int main()
{
int num, x, sqsm, psqsm;
char further;
sqsm = 0;
do
{
printf("Enter a natural number-");
scanf("%d", &num);
x = num * num;
do
{
sqsm = sqsm + x;
x = (sqrt(x) - 1) * (sqrt(x) - 1);
} while(x > 0);
printf("Sum of squares of first %d natural numbers=%d\n",num,sqsm);
printf("Do you want to enter another number(y/n)-");
fflush(stdin);
scanf("%c",&further);
} while(further == 'y');
}

Use
scanf(" %c",&further);
^^^
otherwise white space characters will be read.
Also remove this statement
fflush(stdin);
because such a call results in undefined behavior.

If you would see in the documentation of fflush() you would find this :
For input streams associated with seekable files (e.g., disk files,
but not pipes or
terminals), fflush() discards any buffered data that has been fetched from the
underlying file, but has not been consumed by the application.
This actually means the fflush() is definitely defined well for input streams but with the given limitation of pipes and terminals.
In your example, the stdin is attached to a terminal stream (it is the default, you can even redirect/change it) and therefore using fflush(stdin) is actually undefined behaviour.
On the other hand, if you closely look into scanf()'s documentation, you will find this :
ยท A sequence of white-space characters (space, tab, newline,
etc.; see isspace(3)). This directive matches any amount of white space, including none,
in the input.
What it actually says is scanf("<a space character><your string>") can directly be used to flush (or read and ignore, to be accurate) the newline characters (and others stated in the docs).
So, in your example, using scanf(" %c", &further)(with the space before %c) will actually produce the expected behaviour.

Related

The following code for finding the factorial of a number goes into an infinite loop on Xcode.There is no output shown after taking the number as input

#include <stdio.h>
int main()
{
int n,fact = 1;
printf("Enter a number\n");
scanf("%d\n",&n);
while(n > 0)
{
fact = fact * n;
n--;
}
printf("Factorial is: %d",fact);
return 0;
}
Please help me find the error with this. I have tried using long and unsigned long int but it still doesn't work.
scanf("%d\n",&n);
// ^^
This will read an integer, and then as much whitespace as it can. It does this by reading characters until it finds the first non-whitespace character then pushes that back on to the input stream for the next read. From the ISO C11 standard 7.21.6.2 The fscanf function /5:
A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read. The directive never fails.
So, after you enter the integer, your code is still waiting around for the first non-whitespace character. If you actually enter a character (say, x) followed by ENTER, you'll see it continue.
Solution is therefore just to get rid of the \n at the end of the format string.
Two other suggestions, not related to your problem but useful to know:
use one of the canonical main formats, the one you need for this code is int main(void).
put a \n at the end of your printf format string so that it formats the output better.

Missing value with scanf in C89

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.

Write int structure data type to file in c

A bit of background to my program. I'm asking the user to input coordinates for two different colour crystals. This will be held in a structure and then written to file, to be read back later by the program (function yet to be written).
I have had a look on forums and blogs but I seem to be only finding results relating to character arrays, whereas I am using an int array.
My code works fine until the writing of the data to the file. (this is the snippet of code)
char fname[20];
FILE *fp;
int loop;
struct coord{
int x;
int y;
};
for(loop=0;loop<3;loop++){
printf("Enter MAGENTA X coordinate: \n");
scanf("%2d",&mg[loop].x);
printf("Enter MAGENTA Y coordinate: \n");
scanf("%2d",&mg[loop].y);
printf("\n\n");
}
for(loop=0;loop<3;loop++){
printf("Enter YELLOW X coordinate: \n");
scanf("%2d",&ylw[loop].x);
printf("Enter YELLOW Y coordinate: \n");
scanf("%2d",&ylw[loop].y);
printf("\n\n");
}
clrscr();
printf("\nDetail entered:");
printf("\n\n\tMagenta\t\tYellow\n");
for(loop=0;loop<3;loop++){
printf("\tx %d,%d y\tx %d,%d y\n",mg[loop].x,mg[loop].y,ylw[loop].x,mg[loop].y);
}
printf("\n\nPlease save your data. Enter file name: ");
gets(fname);
fp=fopen(fname,"w");
for(loop=0;loop<3;loop++){
fprintf(fp,"%d,%d ",mg[loop].x,mg[loop].y);
fprintf(fp,"%d,%d ",ylw[loop].x,ylw[loop].y);
}
fclose(fp);
Thank you in advance.
There's nothing wrong with how you write the data to the file (maybe except that you should check the return value of fopen.)
Your problems are in the input of your data from stdin. First, your read in a bunch of coordinates with scanf. scanf is not a line-based format; it treats a new-line character like any other white-space.
Then you use gets, which you really shouldn't - use fgets instead, because it allows you to specify a maximum buffer size so that you don't overflow your buffer of 20.
Okay, say you use fgets, which is a line-based format. You have to take care when mixing scanf and fgets, because they work differently. Also input from the console is usually buffered until the next newline, when it is all flushed, which can create a discrepancy between what you see on the screen and how the program processes input.
Say you read two numbers with scanf and then a filename with fgets. Your input looks like this:
1 2 \n 5 1 \n d a t a . t x t
a a b b b c
After reading the first number the input marker is after the number, before the newline. scanf skips white space before numbners read with "%d", so the second number is read in, but again, input stops before the newline. Then you read a line with fgets and read everything up to the next newline, which is nothing. The real filename would be read by the next scanning function, but there isn't any, so it's discarded.
(That's where your str != NULL is coming from: it's an assertion that has failed withinfopen`. This is nopt something that the compiler does, though. When you run the program, it has already been compiled. The run-time library does such error checking of conditions it can't know at compile time.)
(What's worse: fgets, unlike gets, retains the newline after the read string. You shoud remove that and possibly other leading and trailing white-space, too. Otherwise you'll end up with a file name that has a new-line in it, which is not funny.)
Anyway, how can you fix this? Change your scanf formats from "%d" to "%d " with a trailing space. "Space" has a meaning to scanf: read any amount of white space.
Alternatively, you could stick to scanf and read your filname with scanf("%19s ", filename). That has the benefit of reading a string without any whitespace in it.
Reportedly you have fixed this by calling fflush(stdin) before reading the filename. The behaviour of fflush on input streams is implementation-dependent and not guaranteed by the standard. If it worked for you, leave it at that.

How does printf and scanf work in a loop? Why I dont need \n in scanf?

I dont really uderstand the code below. How does it work exactly (I/O buffers I mean). I dont need a \ncharacter in my code an it still works! Can anyone explain it to me step by step?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int x = -1;
do
{
printf("Give x: ");
scanf("%d", &x);
}while(x<=0);
printf("x = %d\n", x);
x = -1;
while(x<=0)
{
printf("Give x: ");
scanf("%d", &x);
}
printf("x = %d\n", x);
return 0;
}
According to scanf's documentation on cplusplus.com:
Whitespace character: the function will read and ignore any whitespace characters
encountered before the next non-whitespace character (whitespace characters include
spaces, newline and tab characters -- see isspace). A single whitespace in the format
string validates any quantity of whitespace characters extracted from the stream
including none).
This is why you don't need to specify the \n in scanf the next scanf call will simply ignore it.
Stripping away the parts OP likely understand, look at the scanf() calls.
The "%d" format specifier says to scan though optional whitespace (space, tab, \n, etc.) and then scan an int. This typically continues until a character is encountered that does not belong to the int. Then that character is "ungotten" (put back in the input buffer).
Say your input was " 123 -456". After the first while loop " -456" would remain in the input buffer. The second while loop would consume the " -456". Assuming that stdin was closed after the -456, scanf() would then detect there is no more data and set x to the value -456. As x is still negative, the second while loop performs scanf() again. This time, no data and scanf() does not change x and returns EOF, which sadly is not monitored. Result: endless loop.
Now try " 123a 456". After the first while loop "a 456" would remain in the input buffer. The second while loop would call scanf() and fail to convert anything as a does not begin a number - thus x remains -1. scanf() would return 0, which unfortunately is not monitoring. The a, not begin consumed, would remain in the input buffer. The 2nd while loop again calls scanf() which would do the same thing resulting in an endless loop.
do {
...
scanf("%d", &x);
} while (x<=0);
...
x = -1;
while (x<=0) {
...
scanf("%d", &x);
}
Far better to use fgets()/sscanf() pair for input from user input.
(User input is evil!)

Using scanf before for loop with fgets returns a[0] as empty

So I can't really figure out why scanf is messing up a[0]. When I print out the array, a[0] is empty and where I want a[0] it is stored in a[1].
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *a[128];
int i;
int num;
scanf("%d", &num);
for (i = 0; i < 2; i++) {
a[i] = malloc(50*sizeof(char));
fgets(a[i], 100, stdin);
}
printf("[0] = %s [1] = %s", a[0], a[1]);
return 0;
}
Your scanf() is not messing up your a[0], at least not in the way you seem to think, by somehow corrupting its storage area. This is very much an issue with mixing different "methods" of input.
What is actually happening with yourscanf/fgets combo is that the scanf is getting the integer, but not reading the newline that you enter at the end of that number, so the first fgets picks that up.
The ISO standard states it thus (in the specified sequence):
Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a [, c, or n specifier.
An input item is read from the stream, unless the specification includes an n specifier. An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence.
The first character, if any, after the input item remains unread.
That first and last paragraph is important. The first means the leading whitespace is skipped, the last means that trailing whitespace is left in the input stream to be read later.
However, I'll tell you what may mess it up (depending on the lengths of your input lines), it's the fact that you allocate 50 characters space for each a[i] then read up to 100 characters into it. Thats undefined behaviour right there.
If you want a robust input solution that handles buffer sizes correctly, detects lines that are too long, throws away the remainders of those lines and so on, see this answer.
Alternatively, if you want to emulate line-based behaviour with the non-line-based scanf (and you're sure that your lines are always less than 100 bytes), you can change:
scanf("%d", &num);
into:
char junk[100];
scanf ("%d", &num);
fgets (junk, sizeof (junk), stdin);
so that it grabs and throws away the rest of the line.
And you should ensure that the maximum-size argument to fgets matches the amount of memory available (such as by using sizeof in the above code).
One other thing, please don't complicate code by multiplying things by sizeof(char) - that value is always 1 as per the standard.

Resources