Is populating an integer array the same as populating a float one? - c

I'm just getting introduced to C, and I was assigned to write a program that would mimic a self check out line at a grocery store. This involves me having to populate an array with the prices of grocery items based on user input, and add them up and copy them to a file.
the easiest way to populate an integer array is with a for loop. But would that be different for an array of type float?
would it look something like this? Or is this incorrect?
int size, i;
float items[size];
printf("How many items are you checking out today?");
scanf("%d", &size);
for(i=0;i<size;i++){
printf("Enter the price of an item\n");
scanf("%f", items[i]);
}
I'm new to this site so thanks in advance

I would recommend always initializing variables as you declare them to prevent "garbage" values by accident. Also, I don't really recommend pre-declaring your loop counters. You see it in a lot of old code (it used to be required due to compiler limitations), but now I just think it's code noise. It would look like this:
for (int i = 0; i < size; i++) {
// stuff
}
Also, your code has a big problem. You're using what's known as a variable-size array, and they are not a good idea. You generally want to either declare the array size at compile time, or dynamically allocate the space for the array using malloc.
Going back to the initalization, though, this is how you would set every element in a stack-allocated array on declaration:
#define SIZE 4
int main(void)
{
float items[SIZE] = { 0 };
}
If you dynamically allocate the array, I recommend using calloc or memset to set the array elements to a default value for the same reason.
To answer your question about populating the array, yes, there is no difference as to how you would actually go about doing it. A for loop works just fine in both cases. Just remember to check the return value of scanf.

As has been correctly pointed out, you cannot declare float items[size]; until size has been validly initialized to a positive integer value. Your attempt to declare items before size contains a value invokes Undefined Behavior due to your attempt to access an uninitialized value. (the valid operation of your code is over at that point and it could do anything from appearing to run correctly to StackOverflow, or SegFaulting)
Any time you are taking user-input, you must account for each character that remains in the input buffer (stdin here). This is especially true when taking input with scanf (or family) due to the way scanf handles input or matching failures. When either occurs, no further characters are read, and any offending characters are left unread in the input buffer -- just waiting to bite you again on your next attempted read (generally resulting in an infinite loop if you are taking input within a loop)
(this is one of the primary reason a line-oriented function such as fgets is recommended for taking user input)
scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions
(return == EOF) the user canceling input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10);
(return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, etc..); and
otherwise, you must handle the matching or input failure and you must account for every character that may be left in your input buffer. (generally you will scan forward in the input buffer until a '\n' or EOF is found discarding any extraneous characters that remain)
If you do your job, you can successfully use scanf as needed.
Next, a general caution do not using floating point for currency (people get real mad when you start losing money due to rounding errors) While it is fine for your example program -- just understand, in a real currency handling program, you would handle currency as an unsigned value multiplied by 100 (or whatever is required) to insure all amounts can be represented exactly.
Putting the scanf requirements together, you could do something like the following safely:
#include <stdio.h>
/* function to empty extraneous characters from stdin
* (like the '\n' generated by pressing [Enter])
*/
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int size = 0, i;
float total = 0.0;
for (;;) { /* loop continually until valid size entered */
int rtn;
printf ("How many items are you checking out today?: ");
rtn = scanf ("%d", &size);
if (rtn == EOF) { /* handle EOF */
fprintf (stderr, "(user canceled input)\n");
return 1;
}
else if (rtn == 1 && size > 0) { /* valid int received */
empty_stdin();
break;
} /* otherwise, handle error */
fprintf (stderr, "error: invalid input.\n\n");
empty_stdin (); /* remove any chars from stdin up to '\n' */
}
float items[size]; /* declare VLA of size floats */
for (i = 0; i < size; i++) {
items[i] = 0.0; /* initialize each (or memset VLA) */
for (;;) { /* loop continually until valid item price entered */
int rtn;
printf (" price of item[%2d]: ", i + 1); /* prompt for price */
rtn = scanf ("%f", &items[i]);
if (rtn == EOF) { /* handle EOF */
fprintf (stderr, "(user canceled input)\n");
return 1;
}
else if (rtn == 1 && items[i] > 0) { /* valid price received */
empty_stdin();
break;
} /* otherwise, handle error */
fprintf (stderr, "error: invalid input.\n\n");
empty_stdin (); /* remove any chars from stdin up to '\n' */
}
total += items[i];
}
printf ("\ntotal (%d items): $%.2f\n", size, total);
}
Example Use/Output
(shown with intentional errors in entry)
$ ./bin/checkout
How many items are you checking out today?: what?
error: invalid input.
How many items are you checking out today?: 4
price of item[ 1]: free?
error: invalid input.
price of item[ 1]: 1.25
price of item[ 2]: 3.50
price of item[ 3]: discount?
error: invalid input.
price of item[ 3]: 2.25
price of item[ 4]: 3
total (4 items): $10.00
Look things over and let me know if you have further questions.

There is no difference in usage of arrays in terms of usage. But there are few changes required in your code.
#define MAX_SIZE (10)
int size=0, i=0; //It is always better to initialize the variables.
float items[MAX_SIZE] = {0.0f}; //Automatically the entire array will be initialized to zero.
printf("How many items are you checking out today?");
scanf("%d", &size);
if(size > MAX_SIZE)
size = MAX_SIZE;
for(i=0;i<size;i++){
printf("Enter the price of an item\n");
scanf("%f", &items[i]); //You need to pass address of variable to scanf
}
There are other ways to implement your code to handle array size. This is one of the way.

Related

Trying to read a positive value in c

i just want to make sure that i read a positive value for x not sure whats wrong here
#include <stdio.h>
void main() {
unsigned x;
int i;
do {
printf("donner un nombre\n");
scanf("%u", &x);
} while (x <= 1);
/*
for (i = 0; i++; i < x) {
int prime = 0;
int x = 2;
do {
if (i % x == 0 ) {
prime = 1;
}
x++;
} while (prime == 0 && x < i);
if (prime == 0) {
printf("%i",i);
}
}
*/
}
Unsigned is bad choice
First at all you change unsigned type to integer int, that alloved you to scan negative numbers such as -1, -5,...
Wrong condition in loop
Also there is bug in do while loop, where you scan until the x <= 1 but number 1 is positive number, so you have to change it to x < 1
Final code will looks like this:
#include <stdio.h>
int main() {
int x;
do {
printf("donner un nombre\n");
scanf("%u", &x);
} while (x < 1);
// your code working with positive x
return 0;
}
Reading input, especially when it involves a numeric conversion is something done time and time again. It's worth taking the time to understand how to do it properly.
For starters, using scanf() for user-input can be done, but it is so full of pitfalls for the new C programmer that it is highly discouraged. This is due to the state that scanf() leaves your input buffer in both after good input (with potential additional characters and the '\n' left unread), and after invalid input (where all characters from the point of matching-failure forward are left unread). To avoid all of these pitfalls, simply read all input into a sufficiently sized buffer (character array) with fgets() and then convert the contents of the array as needed.
Why fgets() and a sufficiently sized buffer? fgets() reads an entire line of input (including the trialing '\n' produced by the user pressing Enter ensuring the input buffer (stdin in your case) is left in a proper state for your next read.
To convert the contents of the array to an integer value, strtol() provides the most robust diagnostics and error reporting in the event the user failed to enter a valid integer, but using sscanf() for the conversion at least provides a valid success/failed indication through its return.
(you couldn't have used scanf() correctly to begin with without checking the return)
How To Read User Input?
The approach is simple, and it virtually the same for any input needed. You simply:
loop continually prompting for input until a valid input is provided by the user (your read-loop),
read the user input into a sufficiently sized buffer (don't skimp on buffer size),
check if the user canceled input by generating a manual EOF by pressing Ctrl + d (or Ctrl + z on windows),
attempt conversion of the contents of your buffer as needed (to int in your case) checking the return of the conversion function used,
if the input is valid, check against any other constraints you have (positive int in your case)
if all conditions for good input met -- break out of the read-loop, otherwise,
handle the invalid input, and loop again.
No matter what you need to get from the user, you can use this approach. It's something you use so often, it makes sense to put it in a function you can call any time you need that type of input. For an int value, where you can provide an optional prompt to the user to be displayed, you can do something similar to:
...
#define MAXC 256 /* if you need a constant, #define one (or more) */
/* read integer value from 'fp', update value at 'val' address.
* returns 1 on success, 0 if user cancels input with ctrl+d (or
* ctrl+z on windows)
*/
int read_int (int *val, FILE *fp, const char *prompt)
{
char buf[MAXC]; /* buffer (char array) to hold line of input */
for (;;) { /* loop continually until valid input or user cancels */
if (prompt) { /* if prompt not NULL, display */
fputs (prompt, stdout);
}
if (!fgets (buf, MAXC, fp)) { /* read/validate line of input */
puts ("(user canceled input)");
return 0;
}
/* attempt conversion to int validating return/handle error */
if (sscanf (buf, "%d", val) != 1) {
fputs (" error: invalid integer input.\n", stderr);
}
else { /* otherwise, valid int received */
break; /* break input loop */
}
}
return 1; /* return success */
}
The function above will require the user to enter a valid integer, looping displaying the prompt and handling any input errors until the user does while still allowing the user to cancel input with a manual EOF. What it doesn't do is apply your constraint of requiring a positive value. This you add as an additional requirement where you call read_int() in your code.
For example, in main()1 since you need a positive value, you simply call the read_int() function in another continual loop until a positive value is returned (handling the manual EOF case as well). That can look like the following:
int main (void) { /* proper invocation of main unless on freestanding sys */
int x = 0;
for (;;) { /* loop continually prompting for input until x positive */
/* validate return of read_int() checking if user canceled */
if (read_int (&x, stdin, "donner un nombre: ") == 0) {
return 0; /* graceful exit on cancel -- not an error */
}
if (x > 0) { /* test x in acceptable range (positive) */
break; /* break read loop */
}
/* handle x outside of range */
fputs (" error: invalid input, must be postive integer.\n", stderr);
}
printf ("\npostivie x: %d\n", x);
}
(note: above you simply apply a continual loop again, looping until the int value is in the range of positive integers -- handle the 0 case however you want to consider it)
If you need to cover the entire range of unsigned, then change the function to be read_long() (and change the types appropriately). Then in main() you can #include <limits.h> and test if (0 < x && x <= UINT_MAX) for input in range.
Putting it altogether simply requires adding #include <stdio.h> at the top, e.g.
#include <stdio.h>
#define MAXC 256 /* if you need a constant, #define one (or more) */
/* read integer value from 'fp', update value at 'val' address.
* returns 1 on success, 0 if user cancels input with ctrl+d (or
* ctrl+z on windows)
*/
int read_int (int *val, FILE *fp, const char *prompt)
{
char buf[MAXC]; /* buffer (char array) to hold line of input */
for (;;) { /* loop continually until valid input or user cancels */
if (prompt) { /* if prompt not NULL, display */
fputs (prompt, stdout);
}
if (!fgets (buf, MAXC, fp)) { /* read/validate line of input */
puts ("(user canceled input)");
return 0;
}
/* attempt conversion to int validating return/handle error */
if (sscanf (buf, "%d", val) != 1) {
fputs (" error: invalid integer input.\n", stderr);
}
else { /* otherwise, valid int received */
break; /* break input loop */
}
}
return 1; /* return success */
}
int main (void) { /* proper invocation of main unless on freestanding sys */
int x = 0;
for (;;) { /* loop continually prompting for input until x positive */
/* validate return of read_int() checking if user canceled */
if (read_int (&x, stdin, "donner un nombre: ") == 0) {
return 0; /* graceful exit on cancel -- not an error */
}
if (x > 0) { /* test x in acceptable range (positive) */
break; /* break read loop */
}
/* handle x outside of range */
fputs (" error: invalid input, must be postive integer.\n", stderr);
}
printf ("\npostivie x: %d\n", x);
}
Example Use/Output
Now that you have written your input routine -- go try and break it, every way you can think of -- and make sure it handles all cases correctly. Then there will always be one or two corner-cases you didn't think about you will have to fix later.
Does the input routine work, requiring a positive value?
$ ./bin/read_int_function
donner un nombre: 0
error: invalid input, must be postive integer.
donner un nombre: banannas and pickles
error: invalid integer input.
donner un nombre: ten
error: invalid integer input.
donner un nombre: 10
postivie x: 10
What about handling the user canceling input?
$ ./bin/read_int_function
donner un nombre: foo
error: invalid integer input.
donner un nombre: (user canceled input)
Handling user-input is one area that gives new C programmers no end of grief. Best to take the time and think through it now, learning how to correctly approach it. Doing so will save you a great deal of time and frustration.
Let me know if you have further questions.
Footnotes:
1.) Unless you are programming in a freestanding environment (without the benefit of any OS), in a conforming implementation, the allowable declarations for main for are int main (void) and int main (int argc, char *argv[]) (which you will see written with the equivalent char **argv). See: C11 Standard - ยง5.1.2.2.1 Program startup(p1). See also: What should main() return in C and C++? In a freestanding environment, the name and type of the function called at program startup are implementation-defined. See: 5.1.2.1 Freestanding environment
If you are using Linux as a development environment, you can take a look at man 3 scanf. This will give you the function's documentation.
That being said, the function returns a number which indicates how many valid inputs were read.
Knowing that, you can read a positive integer with the following code to check for read status:
int rc;
unsigned int x;
rc = scanf("%u", &x);
if (rc == 1) {
// valid input data
}

I want to write a C program to add an arbitrary list of numbers given by user...can anyone fix this program?

I want to write a C program to add the numbers given by the user as long as they want... can anyone fix this program?
I tried to use a do-while loop.
Any other suggestions to improve my code?
I am unable to end the loop.
#include <stdio.h>
int main()
{
int x=0, sum = 0, y=0, fu;
printf("first number you want to add:\n");
scanf("%d", &x);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
do
{
printf("do you want to add numbers further? \nEnter 0:Yes or 1:No: \n");
scanf("%d", &fu);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
while(fu>0);
sum=x;
printf("Sum of all integers = %d\n", sum);
return 0;
}
Ask for the 3rd and further numbers in an if and modify your while:
scanf("%d", &fu);
if(fu == 0) {
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
}
while(fu == 0);
Your prompt says:
Enter 0:Yes or 1:No:
so you need to continue that loop if 0 was entered:
while(fu == 0);
Also, you don't need to take another y after non-0 input.
The key to taking any input, either from the user, or from a file, is to validate every single input by checking the return. You cannot blindly use a variable holding input until you know whether the input succeeded or failed. Otherwise, if the input fails and you use a variable whose value is indeterminate, you invoke undefined behavior.
Also, if you are using a formatted input function such as scanf(), if a matching failure occurs, character extraction from stdin ceases at that point and the characters causing the failure are left in stdin -- unread, just waiting to bite you again on your next attempted input.
Instead, if you use a line-oriented input function such as fgets() or POSIX getline(), you read an entire line at a time. You can simply call sscanf() on the buffer filled by fgets() to convert a numeric input to an integer value. That way it does not matter if the conversion succeeds or fails, you do not leave anything unread in the input stream.
Just as you must validate every input, you so too must validate every conversion. Whether using sscanf() or strtol(), etc... a failure to validate every conversion will likely lead to undefined behavior when you fail to detect the conversion failure.
Another benefit of using fgets() or getline() is they read and store the '\n' from the user pressing Enter. So rather than having to prompt "do you want to add numbers further? \nEnter 0:Yes or 1:No: \n" and have to worry about yet another input and conversion -- you simply check if Enter was pressed on an empty line to know the user completed input (e.g. the first character in the buffer filed by fgets() is the '\n' character).
You also have to handle an invalid input correctly. What if the user enters "bananas" instead of a number?
Putting it altogether, you could do something similar to:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer (character array) to hold all user input */
int sum = 0, n = 0; /* sum and count of numbers */
puts ("press ENTER alone to exit:\n"); /* display instructions */
while (1) { /* loop continually */
int tmp; /* temporary int to add to sum */
/* prompt based on 1st or subsequent number */
fputs (n ? "next number : " : "first number : ", stdout);
/* read and validate input, break on EOF or empty line */
if (!fgets (buf, MAXC, stdin) || *buf == '\n') {
puts ("---------------------");
break;
}
/* validate conversion to int */
if (sscanf (buf, "%d", &tmp) == 1) { /* on success */
sum += tmp; /* add to sum */
n += 1; /* increment count */
}
else /* handle error */
fputs (" error: invalid integer input.\n", stderr);
}
printf (" sum : %d\n", sum); /* output final sum */
}
Example Use/Output
$ ./bin/sum
press ENTER alone to exit:
first number : 10
next number : -20
next number : 30
next number : -40
next number : bananas
error: invalid integer input.
next number : 50
next number :
---------------------
sum : 30
There are several ways to approach this, and if you wanted the user to be able to enter more than one-number per-line, you could parse buf with strtol() to extract all values. (you can do the same with sscanf() using an offset from the beginning of the string and the characters consumed on each conversion from the "%n" specifier) Many ways to go.
Let me know if you have further questions.

How do I limit the input of scanf to integers and floats (numbers in general)

I am curently learning C Programming in University and I got the task to write a program witch puts out the interception points with 0 / x-axis of any function ax^2+bx+c.
In order to make sure that the input is correct (int, float, etc.) I run the following while loop. Prior to that a is definded as a double.
printf("Input for a=");
while (scanf("%lf", &a) == 0)
{
fflush(stdin);
scanf("%lf", &a);
printf("Incorrect Input! New Input needed.\n");
printf("a=");
}
I am aware that the fflush(stdin) operator only clears the buffer when a second input function occurs and therefore the fflush inside the loop does not clear the buffer and therefore the condition of the loop is always true and thus I created an infinite loop.
My professor also forbids the use of goto, which is why I am here, because I can't come up with a reasonable solution that solves this problem.
I also tried and failed with:
do
{
printf("\nInput for a= ");
scanf("%lf", &a);
}
while (isdigit(a));
{
printf("Thank you.\n");
}
With this arrangement I get the failure notification: Expression c >= -1 && <= 255. I guess this has to do with false variable definition (double, digit) or such.
However my original question was whether there is an elegant solution to this problem or at least any solution.
Lukas, I'm still not 100% clear and your:
"Im am asking how to distinguish between all numbers and every other possible input for scanf."
scanf can provide conversion to a single given type based on the conversion specifier used, so you can't read both int and float with the same scanf statement and a single conversion specifier. (now you can read a line with, e.g. fgets and then use alternate sscanf statements and check the remaining characters to do that)
That said, I think I understand what you are asking and can answer both the fflush and read of a value questions.
To begin, when using scanf you must check the return and handle three-cases. (1) EOF, the user cancels input with a Ctrl+d (or Ctrl+z on windows); (2) a matching or input failure occurs resulting in a return of less than the number of conversion specifiers used; and finally (3) the good input case (where you impose any additional checks you have, e.g. positive, less than 100, etc..)
While fflush(stdin) is undefined behavior on most systems, there are a number of implementations that allow it. (primarily windows, but Linux allows it for seekable stream, e.g. a file redirected on stdin) Bottom line, it isn't portable without caveats, so it's best to provide a simple equivalent with getchar(), e.g.
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
As for scanf, as I mentioned in the comment, it is far easier to enclose the scanf call within an infinite loop which you only break when the input satisfies all your constraints. A simple example requiring integer input would be:
int getint (int *value, const char *prompt)
{
/* loop continually until good input or canceled */
for (;;) {
int rtn;
fputs (prompt, stdout); /* display prompt */
rtn = scanf ("%d", value);
if (rtn == EOF) { /* user generated manual EOF */
fputs ("<user canceled input>\n", stderr);
return 0;
}
empty_stdin(); /* all other cases - empty input buffer */
if (rtn == 1) /* good input, break */
break;
/* otherwise matching failure */
fputs (" error: invalid integer input.\n", stderr);
}
return *value; /* value also availale through pointer */
}
Putting it altogether in a simple example, you would have:
#include <stdio.h>
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int getint (int *value, const char *prompt)
{
/* loop continually until good input or canceled */
for (;;) {
int rtn;
fputs (prompt, stdout); /* display prompt */
rtn = scanf ("%d", value);
if (rtn == EOF) { /* user generated manual EOF */
fputs ("<user canceled input>\n", stderr);
return 0;
}
empty_stdin(); /* all other cases - empty input buffer */
if (rtn == 1) /* good input, break */
break;
/* otherwise matching failure */
fputs (" error: invalid integer input.\n", stderr);
}
return *value; /* value also availale through pointer */
}
int main (void) {
int v,
i = getint (&v, "enter integer value: ");
if (i)
printf ("\ninteger: %d\n", v);
return 0;
}
Example Use/Output
Where you can now do your best to break any input routine you write. If you find a problem, go fix it and try to break it again.
The code above allows for fairly robust input of any one given type of value, e.g.
$ ./bin/scanfint
enter integer value: no
error: invalid integer input.
enter integer value: apples, banannas, and pears
error: invalid integer input.
enter integer value: 21
integer: 21
Look things over and let me know if your question was slightly different, or if you have further questions about the answer.

Requesting user input in C

I'm new to C programming and i find prompting for user input quite a challenge for beginners like me.
I'm just writing a simple C code to continuously prompt for user input until the user enters a negative number which stops the program. In a way, i want the format of prompting user input similar to the way we used to do in python where:
Enter a number: (userinput a number here and press enter)
and the output will be:
The number you entered is 10 (for example)
which doesn't yet stop the loop and prompts for another number since no negative number is entered.
Enter a number: (userinput another number which is negative)
Output:
The number you entered is -10 (for example)
and the program stops.
I tried writing in C:
#include <stdio.h>
int value = 0;
while (value >= 0) {
do {
printf("Enter a number: ");
scanf("%d",&value);
printf("The number you entered is %d", &value);
}
but i can't seem to get the "Enter a number:" statement to display first when i run the program as it immediately request for user input at the start without the prompting message displayed until when i enter an integer, the prompting message displays which is the opposite of what i wanted. Would appreciate some help on this.
One of the biggest problems faced by new C programmers is handling user input correctly, especially when using the scanf family of functions. Why? When using scanf you must account for all characters left in the input buffer (stdin here) in the event of a matching failure. Why? When a matching failure occurs, scanf stops processing characters at the point of failure, and the character(s) that caused the failure remain in your input buffer unread, just waiting to bite you on your next attempted input.
Further complicating the issue is how the difference scanf conversion specifiers treat whitespace. Your numeric input specifiers and %s will consume leading whitespace, while the remainder of the conversion specifiers don't. That means if you are taking input with other than a numeric or %s conversion specifier -- you must account for and remove the trailing '\n' before attempting the next character or character class conversion.
That said, whenever you use scanf, it is up to you to check the return so you can determine whether the user canceled input with a manually generated EOF of whether a matching or input failure occurred.
At the bare-minimum, you must check the return and handle any return indicating less than the number of expected conversions took place. In your case, with one conversion to int, you must check that the return is 1 before making use of value. Otherwise you can easily invoke Undefined Behavior. Now just checking whether all conversions occurred does not allow you to discriminate between EOF or matching failure - leaving you without the information needed to proceed making the best you can do is to exit on invalid input, e.g.
#include <stdio.h>
int main (void) {
int value = 0;
while (value >= 0) {
printf("Enter a number: ");
if (scanf("%d",&value) != 1) {
fputs ("error: invalid input\n", stderr);
return 1;
}
printf("The number you entered is %d\n", value);
}
return 0;
}
Example Use/Output
$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input
(note: on entry of an invalid integer, all the program can do is end, you do not know whether the input was canceled or whether a matching failure occurred which would allow you to take appropriate action to continue by emptying the input buffer if the failure was a matching failure.)
The proper way to handle input with scanf is to cover all potential error conditions and to gracefully respond to a matching failure by clearing the input buffer of the offending characters allowing you to continue with input. It helps to have a small helper-function to clear stdin rather than having to repeatedly include a clearing loop every where scanf is used in your code. A short example would be:
/** remove all characters that remain in stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Which simply reads all characters from stdin until the '\n' or EOF is encountered. Combining that with an expanded check of the return of scanf will allow you to handle a matching failure gracefully, e.g.
#include <stdio.h>
/** remove all characters that remain in stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int value = 0;
while (value >= 0) {
int rtn; /* variable to store return on scanf */
printf ("Enter a number: ");
rtn = scanf ("%d",&value);
if (rtn == EOF) { /* handle EOF */
fputs ("user canceled input.\n", stderr);
break;
}
else if (rtn == 0) { /* handle matching/input failure */
fputs ("error: invalid input\n", stderr);
empty_stdin();
}
else /* good input - output value */
printf("The number you entered is %d\n", value);
}
return 0;
}
Example Use/Output
$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input
Enter a number: -1
The number you entered is -1
Here if a non-integer like foo is entered, it is caught, the characters removed from stdin and the loop prompts again for input.
Look things over. C is not python. Python hides much of the implementation details from you to essentially protect you from instances just as this, but it has its drawbacks as well. With C, nothing is hidden from you. You are given free reign to write to memory you don't own, repeatedly attempt to read form an input buffer after a conversion failure, etc.. You are responsible for the details.
Lastly, all of this is the primary reason taking input with fgets or POSIX getline is recommended for new users. With a sufficiently sized buffer (don't skimp on size), fgets will read a line at a time from the input buffer, preventing offending characters remaining just waiting to bite you again. getline will allocate a buffer of sufficient size no matter how long the line is -- but you are responsible for freeing the memory when you are done with it. Investigate both as alternatives to using scanf. You can always call sscanf on the buffer holding the line after it is read to parse numeric values from it.

Stop a for loop when user is finished entering input in c

First of all, thank you for the assist!
I'm new to the C language (and programming in general) and I'm trying to write a program wherein the user inputs data points. The data points are then saved in an array where they can then be manipulated.
Where I am stuck: I want the user to be able to input (almost) any number of points, then use a 'keyword' of sorts to signal the end of data entry. In this case, the user would type 'done'.
Here's what I have so far:
#include <stdio.h>
#include <string.h>
int main(void) {
printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n");
double data[1048];
int i, count;
for (i = 1; ;i++) {
printf("Data[%i]: ", i);
scanf("%lf", &data[i]);
if (data[i] == 'done') {
break;
} else {
count++;
}
}
}
I've tried 'return 1;' and 'break;'. Each time, the program works well until the 'keyword' is entered, at which point I get:
Data[8]: Data[9]: ... Data[1120]: Data[1Segmentation fault 11
The only time it works is if I have it break when the user inputs a particular number (like -1 or 0). But that doesn't quite work for the user since they might have to enter those numbers as data points.
Sorry for the long post, but I appreciate the help!
You have received a number of good answers to your question, and there are several more ways to take input of doubles and stop on "done". Since you are learning C, always, ALWAYS (in case it wasn't clear), check the return of scanf to validate the number of conversions you expected actually took place.[1] (this also provides your way to end input on "done" (or any non-double entered causing scanf to return less than 1)
As noted in the comment, arrays are zero based in C. When you are taking input, you will want to use count as your array-index, rather than i (in this case if you exit the read on each failure -- it doesn't matter, but you could just as easily prompt again for additional input and increment count only on a successful return from scanf) Back to your question. If you set up your read loop to continually loop until there is a scanf failure, you can make use of a temporary variable to initially capture the input value, and only assign the value to your array and increment your index on success. e.g. (with a constant MAXD = 1048)
for (;;) { /* loop until scanf input fails (with 'done') */
double tmp; /* block scope declarations are fine */
printf (" data[%4d]: ", count);
if (count < MAXD && scanf(" %lf", &tmp) == 1)
data[count++] = tmp;
else
break;
}
(you can even move a copy of the prompt above the loop, and move the one above after the if (....) {...} to eliminate the prompt when the array limit (MAXD) is reached -- that's left as an exercise)
In the example above you have 2 conditions you enforce before storing a value. (1) you limit the number of values your user can store to MAXD, and (2) you only store a value if a valid conversion to double takes place in scanf. You leave the loop if either of the conditions fails (which if you enter "done" as a double-value, it will).
Putting the pieces together and dropping a few additional tips in the comments, you could test with something like the following:
#include <stdio.h>
enum { MAXD = 1048 }; /* declare constants instead of using magic numbers */
int main (void) {
double data[MAXD] = {0}; /* in ISO C declarations come before code */
int i, count = 0; /* initializing variable saves debug time */
printf ("\n Welcome! \n\n Please enter each data point. "
"Enter 'done' when finished.\n\n");
for (;;) { /* loop until scanf input fails (with 'done') */
double tmp; /* block scope declarations are fine */
printf (" data[%4d]: ", count);
if (count < MAXD && scanf(" %lf", &tmp) == 1)
data[count++] = tmp;
else
break;
}
printf ("\n %d values entered:\n\n", count);
for (i = 0; i < count; i++)
printf (" data[%4d] : %.2lf\n", i, data[i]);
return 0; /* main() is type 'int' and returns a value */
}
Example Use/Output
$ ./bin/scanfdoubles
Welcome!
Please enter each data point. Enter 'done' when finished.
data[ 0]: 1.1
data[ 1]: 1.2
data[ 2]: 1.3
data[ 3]: 1.4
data[ 4]: 1.5
data[ 5]: 1.6
data[ 6]: done
6 values entered:
data[ 0] : 1.10
data[ 1] : 1.20
data[ 2] : 1.30
data[ 3] : 1.40
data[ 4] : 1.50
data[ 5] : 1.60
Look things over and let me know if you have any questions.
footnotes:
1. while you can use scanf to take user-input in C, you are better off using a line-oriented function (like fgets) and then parsing the complete line (with, e.g. sscanf). The allows you to both (1) validate the read (e.g. the return of fgets) and then (2) separately validate the value entered by the user. This decoupling of your read, and your parsing has many advantages.
No element of data[] will ever be 'done' (they're floats). If you want to scanf() directly, you'll need to choose a double value that ends the sequence (commonly zero or -1 or something). If that won't work, you can either use something like:
Use fgets() to pull a string, then strncmp() to check for the terminating value and sscanf() to pull out the double, or:
Have the user use Ctrl-D to terminate and check the scan value for EOF.
Oh, and strictly speaking you have an upper limit of entries. You should check i to make sure that you don't exceed that. Never assume your input won't exceed boundaries. sizeof() on a statically-allocated variable or some #defined macro to track that.
Your data is of type double. It can't scan a literal "done".
Instead use EOF for checking end of input.
while(scanf("%lf",&data[i]) != EOF) {
...
}
Another way:
while(scanf("%lf",&data[i]) == 1) {
...
}
Another thing, initialize count to zero, i.e. count = 0;
Bottom line: don't use scanf.
Use something like
char inputline[100];
i = 0;
while(fgets(inputline, sizeof(inputline), stdin) != NULL) {
if(strncmp(inputline, "done", 4) == 0) break;
data[i++] = atof(inputline);
}
scanf is hard enough to use even when all your inputs are the numbers you expect. If the input might be either a number or the word "done", scanf will never work. But reading a line of text, as here, is generally easier and more flexible.
P.S. You also have to worry about the possibility that the user enters more than 1048 numbers.
For your task the loop of gathering input should control not only keyword, but also number if inputs. I suggest to do this as follows:
#include <stdio.h>
#include <string.h>
#define NUM_OF_DATA 1048
int main(void)
{
printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n");
double data[NUM_OF_DATA];
int i; // counter of entered numbers
char str[5] = { 0 }; // string to read 'done' or other word from input
for (i = 0; i < NUM_OF_DATA; i++) // the first index of data in array is 0 (the last NUM_OF_DATA-1)
{
printf("Data[%i]: ", i);
if (1 == scanf("%lf", &data[i])) // if number was successfully read
continue; // go to next iteration
// if some problem was with reading a loat number
// read the string
scanf("%4s", str); // read not more than 4 characters from input
if ( strcmp(str, "done") == 0)
{
break; // stop input if 'done' was entered
}
// clean input buffer before next input
while (getchar() != '\n');
// correct counter in case of wrong input
i--;
}
// output the number of correct inputs
printf("%d numbers were entered.\n", i);
// do something with data
// taking in account, that i is not index of the last element,
// but the number of elements (indexes are 0 ... i-1)
// ...
return 0;
}
This for loop stops in two cases:
1) when data array is full,
2) when 'done' without quotes entered.
Additional feature is skipping of incorrect input (try how it works).

Resources