C- Save string to array and print a char - c

Im trying to save a 100 character max string into an array and then print an specified character of the array via an index, yet I get Segmentation error 11, here is the code:`
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str1[100];
int index;
printf("Enter text of max 100 characters: \n");
scanf("%s", str1);
printf("Enter the index to search\n");
scanf("%d", &index);
printf("your char is: %c\n", str1[index]);
return(0);
}
`
Any suggestions?

While user input is generally better handled by reading input with fgets and then parsing what you need from the resulting buffer with sscanf of simply with a pair of pointers and "inch-worming down" (a/k/a "walking") the string testing each char and handling as needed -- it is always worth a look at scanf to detail what you need to do to successfully use it for user input.
While fgets is not without needed validations, the number and types of validations needed with scanf and handling of characters that remain in the input buffer in the different cases of input failure or matching failures create a number of extra pitfalls for new (and not so new) C programmers.
The two primary problems with scanf are (1) there is no default limitation on the number of characters that it will read into any buffer (potentially overflowing your array); and (2) the fact that it does not remove the trailing '\n' (or any of the characters following an input or matching failure) from the input buffer (e.g. stdin). It is up to you to account for all characters in the input buffer and empty the buffer as needed.
Further complicating the picture are the ways the different scanf format specifiers handle leading-whitespace (numeric conversions typically skip leading whitespace, while a character conversion won't) Another issue is handling included whitespace. The "%s" format specifier will only read up to the first whitespace encountered, making it impossible to read "My dog has fleas" with a single %s specifier. (you can use a character class to read included whitespace -- as shown in the example below)
There are many other subtleties with scanf as well, so it is well worth the time it takes to read and understand man scanf.
From the comments, you now know if you ask for a string of 100 chars, you need, at minimum, 101 characters of storage -- we will assume that is learned.
When taking any input with scanf, you must always validate the return to insure that the number of conversion expected, in fact took place. For example, if you are reading "5 dogs" with the conversion specifier "%d %s", a return of 2 indicates a successful conversion to integer and string. However, you also know, at minimum a '\n' remains in the input buffer (and potentially many more characters, if say, "5 dogs and cats" were entered. It is up to you to remove the '\n' and any other characters that remain, before attempting to read more input with scanf.
The following example captures most of the pitfalls with your example and provides a couple of tools you can use when dealing with user input. The bottom line is learn to use fgets, but know how to use scanf as well. Your goal is to provide as robust and bullet-proof input routine as you can. Think about all the dumb things a user might do when prompted for input (or heaven forbid, a cat walks across the keyboard) There are always more validations you can add. Look at each of the included validations, and let me know if you have questions:
#include <stdio.h>
#include <string.h>
#define MAXC 100 /* if you need a constant, declare one */
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char str1[MAXC+1] = ""; /* initialize to all zero */
int index, rtn, len; /* index, return, length */
for (;;) { /* loop until valid input obtained */
printf ("Enter text of max 100 characters: ");
rtn = scanf ("%100[^\n]", str1); /* read at most MAXC char */
if (rtn != 1) { /* validate scanf return */
if (rtn == EOF) { /* check if EOF, ctrl+d, ctrl+z (windoze) */
printf ("input canceled.\n");
return 0;
}
if (!str1[0]) /* was a character entered? */
fprintf (stderr, "error: string is empty.\n");
/* remove '\n' and any chars that remain in stdin */
empty_stdin();
}
else { /* all conditions met, good entry, empty stdin and break */
empty_stdin();
break;
}
}
len = (int)strlen (str1); /* get string length */
for (;;) { /* now do the same thing for integer */
printf ("Enter the index to search (0-%d): ", len - 1);
if ((rtn = scanf ("%d", &index)) != 1) {
if (rtn == EOF) {
printf ("input canceled.\n");
return 0;
}
fprintf (stderr, "error: invalid input - not integer.\n");
/* only need to strip if non-integer entered, because %d
* will skip leading whitespace, including '\n'.
*/
empty_stdin();
}
else if (index < 0 || len < index + 1) /* validate index value */
fprintf (stderr, "error: invalid index - out of range.\n");
else
break;
}
printf ("your char is: %c\n", str1[index]);
return 0;
}
Example Use/Output
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): -1
error: invalid index - out of range.
Enter the index to search (0-19): 0
your char is: 1
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): foo
error: invalid input - not integer.
Enter the index to search (0-19): 6
your char is: 7
$ ./bin/scanfstr1
Enter text of max 100 characters: My dog has fleas.
Enter the index to search (0-16): d
error: invalid input - not integer.
Enter the index to search (0-16): 3
your char is: d
$ ./bin/scanfstr1
Enter text of max 100 characters:
error: string is empty.
Enter text of max 100 characters: My cats are fine.
Enter the index to search (0-16): meow
error: invalid input - not integer.
Enter the index to search (0-16): input canceled.

Related

Reading multiple strings from one line. Detecting invalid input

I am trying to write a program that reads 2 words separated by a single space in a single input line.
Normal input should look like this: Apple Banana. Apple should be written to FirstWord and Banana to SecondWord
Currently I am using scanf and reading strings with it.
That is my code:
int main (void) {
char FirstWord[10];
char SecondWord[10];
while (true) {
printf("Enter two words: ");
scanf("%s", FirstWord);
if(!strcmp(FirstWord, "quit")) {
printf("Exit.");
return 0;
}
scanf("%s", SecondWord);
printf("First word is %s \nSecond word is %s\n", FirstWord, SecondWord);
}
}
Program is working, generally speaking, but there is one problem of detecting invalid input from the user.
How can I detect if user enters only one word or empty line? Right now the second scanf waits for the second string in case if only 1 word entered.
Is it possible to deal with that problem with scanf or should I actually get 1 string with fgets and then divide it into two strings?
When you want the user to enter two-words, you want to also be able to handle the case where the user enters one word (maybe thinking they will enter the next word on the next line, etc...) The problem with using scanf() with the "%s" conversion specifier is it ignores leading whitespace and the '\n' character is whitespace. So if only one word is entered, you do not have to opportunity to prompt for the second word because scanf() will block waiting on the second input.
Any time you are taking user input, you are encouraged to use fgets() to fill a buffer (character array) and then parse (separate) the needed values from the buffer. Why? fgets() will consume an entire line of input (given a sufficiently sized buffer). This provides certainty that nothing will remain in stdin unread --- just waiting to bite you on your next input.
You can then use sscanf() to parse values from the buffer just as you would use scanf() to parse values from input. However, you have decoupled the input and conversion, so if a sscanf() conversion fails it will NOT effect your next attempt to read from stdin.
Using fgets() also provides a convenient way of ending input. Since fgets() reads the '\n' character generated by the user pressing Enter you need only check if the first character in the buffer filled is the '\n' character to know the user has simply pressed Enter on a blank line. This can be used as a very convenient way to determine the user is done without requiring additional input such as strcmp(FirstWord, "quit"), etc...
If I understand you want to be able to seamlessly handle either one or two inputs from the user and prompt in the case the user only provides one, you can do:
#include <stdio.h>
#define WORDSZ 32 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void) {
char FirstWord[WORDSZ]; /* declare arrays */
char SecondWord[WORDSZ];
char buf[MAXC]; /* buffer to hold entire line */
puts ("press [Enter] on empty line when done.");
while (1) { /* loop continually until both filled or user cancels */
fputs ("\nEnter two words: ", stdout); /* prompt both */
if (fgets (buf, MAXC, stdin) == NULL) { /* read into buf, validate */
puts ("(user canceled input)");
return 0;
}
else if (*buf == '\n') /* if [Enter] on empty line */
break;
/* validate conversion, check if only one word read */
if (sscanf (buf, "%31s %31s", FirstWord, SecondWord) == 1) {
while (1) { /* if so, loop until second word read */
fputs ("Enter second word: ", stdout); /* prompt 2nd word */
if (!fgets (buf, MAXC, stdin)) { /* read into buf, validate */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%31s", SecondWord) == 1) /* validate word entered */
break;
}
}
printf ("\nFirst word is %s \nSecond word is %s\n", FirstWord, SecondWord);
}
}
(note: you must always provide the field-width modifier of one less than your array size for all string conversions to protect your array bounds when using the scanf() family of functions. Otherwise the use of scanf() is no safer than using gets(), See Why gets() is so dangerous it should never be used!)
Since the user canceling input by generating a manual EOF is perfectly valid, you need to check for a manual EOF after each read with fgets() by checking if the return is NULL. A manual EOF is generated by the user pressing Ctrl + d (Ctrl + z on windows).
Example Use/Output
$ ./bin/firstsecondword
press [Enter] on empty line when done.
Enter two words: hello
Enter second word: world
First word is hello
Second word is world
Enter two words: hello world
First word is hello
Second word is world
Enter two words:
Look things over and let me know if you have further questions.

Scanf not skipping when I press Enter

The following piece of code is continuously asking for me to give it 1 input, meaning that when I press "Enter" it does not skip to the next scanf instead just goes to the next line on console and waits for input.
int main()
{
int i, print, line ;
char oFile[50] , iFile[50] = "listsource.c" ;
printf("Please enter the name of the input file: ") ;
scanf("%s", iFile) ;
printf("Please enter 0 to print to console, 1 to print to another file: ") ;
scanf("%d", &print) ;
printf("%s", iFile) ;
}
I am trying to give it a default value of "listsource.c" if no input is entered, have already tried fgets and I have the same problem
Try this:
#include <stdio.h>
int main()
{
char hello[81];
fgets(hello, 80, stdin);
hello[strlen(hello) - 1 ] = '\0';
// OR
gets(hello);
}
It worked with Online C Compiler.
You will have to strip the newline although...gets() will do the work, but is it dangerous as it will read any number of character regardless of the amount specified by you when you declared the variable it is going into. Thus it will overwrite past the memory allocated by the compiler. (Yes, this is allowed by C. In C, the programmer is expected to know what he is doing, so he gets all the powers he wants!)
All scanf() conversion specifiers except "%c", "%[..]" and "%n" ignore leading whitespace. A '\n' is whitespace. Using scanf() you can press Enter until your finger falls off using the "%d" conversion specifier and it will never be read. scanf() will simply discard all whitespace and continue blocking waiting on valid input or EOF.
That is just one, of the many, many pitfalls scanf() has for new C programmers. That is why it is highly recommended that you use fgets() for ALL user input. With a sufficient size buffer (character array), fgets() will consume an entire line-at-a-time (including the trailing '\n'). This greatly simplifies input because what remains in stdin after the user presses Enter does not depend on whether a scanf() matching-failure occurred.
To remove the trailing '\n' from the end of the buffer filled by fgets(), simply use strcspn() as follows:
iFile[strcspn (iFile, "\n")] = 0; /* trim \n from end of iFile */
If you do need to convert the contents of your buffer, simply use sscanf() providing your buffer as the first argument, the remainder just as you would use scanf() -- but on any failure, nothing is left in stdin because you have completely read the user-input with fgets().
If you had attempted to read an int with scanf() and the user slipped and hit 'r' reaching for '4', then a matching-failure occurs and character extraction from stdin ceases leaving 'r' in stdin unread. If you are taking input in a loop -- you have just created an infinite loop...
In your case here asking the user to enter 0 or 1, there is no need for numeric conversion to begin with. Simply read the input into a buffer with fgets() and then check if the first character in the buffer is '0' or '1' (the ASCII digits). No conversion required.
Don't use MagicNumbers (e.g. 50) in your code. If you need a constant, #define one, or use a global enum to accomplish the same thing, e.g.
#define MAXFN 50 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void)
{
char buf[MAXC], /* oFile[MAXFN] ,*/ iFile[MAXFN];
(note: if programming on a microcontroller, reduce the max number of characters for your read-buffer (MAXC) accordingly, otherwise, for general PC use a 1K buffer is fine)
Putting it altogether, and adding a "print to another file - not implemented", to handle the user entering 1 as asked, you could do:
#include <stdio.h>
#include <string.h>
#define MAXFN 50 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void)
{
char buf[MAXC], /* oFile[MAXFN] ,*/ iFile[MAXFN];
fputs ("Please enter the name of the input file: ", stdout);
if (!fgets (iFile, MAXFN, stdin)) { /* read ALL user input with fgets() */
puts ("(user cancled input)"); /* validate, if manual EOF return */
return 0;
}
iFile[strcspn (iFile, "\n")] = 0; /* trim \n from end of iFile */
for (;;) { /* loop continually until valid input from user or EOF */
fputs ("\nPlease enter 0 to print to console, "
"1 to print to another file: ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read ALL user input with fgets() */
puts ("(user cancled input)"); /* validate, if manual EOF return */
return 0;
}
if (*buf == '0') { /* no need to covert to int, just check if ASCII '0' */
puts (iFile);
break;
}
else if (*buf == '1') { /* ditto -- just check if ASCII '1' */
puts ("print to another file - not implemented");
break;
}
fputs (" error: invalid input, not 0 or 1\n", stderr); /* handle error */
}
}
(note: when you need the user to provide specific input, loop continually until you get what you require, or until the user generates a manual EOF by pressing Ctrl + d (or Ctrl + z on windows))
Example Use/Output
Intentionally pressing Enter alone for the first input and providing invalid input for the next two, you would have:
$ ./bin/console_or_file
Please enter the name of the input file: myInputFilename.txt
Please enter 0 to print to console, 1 to print to another file:
error: invalid input, not 0 or 1
Please enter 0 to print to console, 1 to print to another file: bananas
error: invalid input, not 0 or 1
Please enter 0 to print to console, 1 to print to another file: 2
error: invalid input, not 0 or 1
Please enter 0 to print to console, 1 to print to another file: 0
myInputFilename.txt
Look things over and let me know if you have further questions.

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.

Using malloc in C to make sure that user enter certain digits

So I want to write a program to check if user enter. Say requirement is 4 digits number, if user enters 5 then program keeps asking user to renter exactly 4 digit number.
I got the working code like this: basically use scanf to read in a string value, then use strlen to count number of digits. If user enters the correct digit, then I use atoi to convert that string into an int, which I will use for later.
Say requirement is 4 digit number:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int digit, mynumber;
int digit = 5;
char str[5];
/* Checking if enter the correct digit */
do {
printf("Enter a %d digit number\n", digit);
scanf("%s", &str);
if (strlen(str) != digit) {
printf("You entered %d digits. Try again \n", strlen(str));
} else {
printf("You entered %d digits. \n", strlen(str));
printf("Converting string to num.....\n");
mynumber = atoi(str);
printf("The number is %d\n", mynumber);
}
} while (strlen(str) != digit);
return 0;
}
I want to modify this a bit. Instead of doing char str[5] for 5 digit string. I want to try a dynamic array.
So in place of char str[5], I do this:
char *str;
str = malloc(sizeof(char) * digit);
Running this through the code gives seg fault. Can anyone help me with this?
This is the complete code with the issue
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int mynumber;
int digit = 5;
char *str;
str = malloc(sizeof(char) * digit);
/* Checking if enter the correct digit */
do {
printf("Enter a %d digit number\n", digit);
scanf("%s", &str);
if (strlen(str) != digit) {
printf("You entered %d digits. Try again \n", strlen(str));
} else {
printf("You entered %d digits. \n", strlen(str));
printf("Converting string to num.....\n");
mynumber = atoi(str);
printf("The number is %d\n", mynumber);
}
} while (strlen(str) != digit);
return 0;
}
While you can use the formatted input function scanf to take your input as a string, scanf is full of a number of pitfalls that can leave stray characters in your input stream (stdin) depending on whether a matching-failure occurs. It also has the limitation using the "%s" conversion specifier of only reading up to the first whitespace. If your user slips and enters "123 45", you read "123", your tests fail, and "45" are left in stdin unread, just waiting to bite you on your next attempted read unless you manually empty stdin.
Further, if you are using "%s" without the field-width modifier -- you might as well be using gets() as scanf will happily read an unlimited number of characters into your 5 or 6 character array, writing beyond your array bounds invoking Undefined Behavior.
A more sound approach is the provide a character buffer large enough to handle whatever the user may enter. (don't Skimp on buffer size). The read an entire line at a time with fgets(), which with a sufficient sized buffer ensure the entire line is consumed eliminating the chance for characters to remain unread in stdin. The only caveat with fgets (and every line-oriented input function like POSIX getline) is the '\n' is also read and included in the buffer filled. You simply trim the '\n' from the end using strcspn() as a convenient method obtaining the number of characters entered at the same time.
(note: you can forego trimming the '\n' if you adjust your tests to include the '\n' in the length you validate against since the conversion to int will ignore the trailing '\n')
Your logic is lacking one other needed check. What if the user enters "123a5"? All 5 characters were entered, but they were not all digits. atoi() has no error reporting capability and will happily convert the string to 123 silently without providing any indication that additional characters remain. You have two-options, either use strtol for the conversion and validate no characters remain, or simply loop over the characters in your string checking each with isdigit() to ensure all digits were entered.
Putting that altogether, you could do something like the following:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define NDIGITS 5 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void) {
int mynumber;
size_t digit = NDIGITS;
char buf[MAXC]; /* buffer to hold MAXC chars */
/* infinite loop until valid string entered, or manual EOF generated */
for (;;) {
size_t len;
printf("\nEnter a %zu digit number: ", digit); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) { /* read entire line */
fputs ("(user canceled input)\n", stdout);
break;
}
buf[(len = strcspn(buf, "\n"))] = 0; /* trim \n, get len */
if (len != digit) { /* validate length */
fprintf(stderr, " error: %zu characters.\n", len);
continue;
}
for (size_t i = 0; i < len; i++) { /* validate all digits */
if (!isdigit(buf[i])) {
fprintf (stderr, " error: buf[%zu] is non-digit '%c'.\n",
i, buf[i]);
goto getnext;
}
}
if (sscanf (buf, "%d", &mynumber) == 1) { /* validate converstion */
printf ("you entered %zu digits, mynumber = %d\n", len, mynumber);
break; /* all criteria met, break loop */
}
getnext:;
}
return 0;
}
Example Use/Output
Whenever you write an input routine, go try and break it. Validate it does what you need it to do and catches the cases you want to protect against (and there will still be more validations you can add). Here, it covers most anticipated abuses:
$ ./bin/only5digits
Enter a 5 digit number: no
error: 2 characters.
Enter a 5 digit number: 123a5
error: buf[3] is non-digit 'a'.
Enter a 5 digit number: 123 45
error: 6 characters.
Enter a 5 digit number: ;alsdhif aij;ioj34 ;alfj a!%#$%$ ("cat steps on keyboard...")
error: 61 characters.
Enter a 5 digit number: 1234
error: 4 characters.
Enter a 5 digit number: 123456
error: 6 characters.
Enter a 5 digit number: 12345
you entered 5 digits, mynumber = 12345
User cancels input with ctrl+d on Linux (or ctrl+z on windows) generating a manual EOF:
$ ./bin/only5digits
Enter a 5 digit number: (user canceled input)
(note: you can add additional checks to see if 1024 or more characters were input -- that is left to you)
This is a slightly different approach to reading input, but from a general rule standpoint, when taking user input, if you ensure you consume an entire line of input, you avoid many of the pitfalls associated with using scanf for that purpose.
Look things over and let me know if you have further questions.
In your code you have an array of 5 bytes. If the user enters more than 4 digits, scanf will happily overflow the array and corrupt the memory. It will corrupt the memory in both cases, as an array and as a malloc. However, not every memory corruption causes a crash.
So, you need to limit the number of bytes the scanf can read. The way to do it is to use %4s in the format string.
However, in this case you will not be able to detect when the user enters more than 4 digits. You would need at least 1 more byte: str[6] and %5s;
I suggest, instead of using scanf, use getchar. It would allow you to read character by character and count them on the way.

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.

Resources