I hope that someone can help me since I'm clueless about the problem in my code. I am a new starter in programming and a week ago I tried an exercise which I completely failed. I tried to compare the elements in the array with the input text on the compiler. But the ("if") is always giving a false. Even if I write down an existing element of the array on the compiler. I know that there must be a lot of logical mistakes but I am really helpless. I am really looking for your answers. Thanks for the help!
#include <stdio.h>
#include <stdlib.h>
struct category {
char item_name[50];
int item_quantity;
float item_price;
};
void items_search (struct category a[], int length)
{
char answer[] = { "yes" };
int number = 0;
int item_quantity = 0;
char name[200];
int i = 0;
printf ("What are you looking for?\n");
scanf ("%s", &name);
if (a[i].item_name == name) {
printf ("What is the desired number of the item?");
scanf ("d", &number);
fflush (stdin);
if (number == a[i].item_quantity) {
printf ("Order is possible. Should the order be placed?\n");
scanf ("%s", &answer);
fflush (stdin);
if (answer == "yes") {
puts ("The order is placed");
}
else {
puts ("Order was cancelled");
}
}
else {
printf ("There are only %d pieces of the item. Should the "
"order be canceled?\n", a[i].item_quantity);
scanf ("%s", &answer);
fflush (stdin);
if (answer == "yes") {
puts ("Order was cancelled. Should be bought later?");
scanf ("%s", &answer);
fflush (stdin);
if (answer == "yes") {
puts ("The order is placed");
}
else {
puts ("The order was cancelled");
}
}
else {
puts ("The order is placed");
}
}
}
else {
printf ("The specified item does not exist\n");
}
}
int main (void)
{
struct category laptops[] = {
{"Asus_365", 7, 499.00},
{"Lenovo_L49", 30, 699.91},
{"HP_Alien", 20, 649.99},
{"Acer Alpha touch", 10, 899.99}
};
items_search (laptops, sizeof (laptops) / sizeof (struct category));
}
You should not compare a pointer to a string:
if(answer=="yes"){
Instead, you should use strncmp:
if(strncmp( answer, "yes", 3) == 0)
For starters these calls of scanf
scanf ("%s", &name);
and
scanf ("%s", &answer);
are incorrect. You should write
scanf ("%s", name);
and
scanf ("%s", answer);
Arrays do not have the comparison operator. So in these if statements
if (a[i].item_name == name) {
and
if (answer == "yes") {
there are compared two pointers to first elements of two different arrays (due to the implicit conversion of array designators to pointers to their first elements) that occupy different extents of memory. So their comparison will always evaluate to logical false.
Instead you need to use the C string function strcmp like
if ( strcmp( a[i].item_name, name ) == 0 ) {
and
if ( strcmp( answer, "yes" ) == 0 ) {
Also calls of the function fflush with the stream stdin
fflush (stdin);
has undefined behavior. Remove such calls.
Pay attention to that the function declaration does not make a great sense relative to its definition because within the function there is used only the first element of the passed array.
There are a large number of small problems in your code. The most critical are the attempts to use == for string-equality instead of strcmp(). The next being the inclusion of '&' before the array name when using scanf(). Upon access, an array is converted to a pointer to its first element, so there is no need for '&' before the array name in scanf() after "%s". See C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
Another issue is you must protect your array bounds when using the "%s" conversion specifier. It cannot be used safely otherwise. Why? "%s" will write as many non-whitespace characters as it is provided to the address given in the argument list. If given 500 bytes of shell-code, it will happily write all 500 bytes corrupting your stack everywhere "%s" is used with scanf(). (especially with answer which you declare as only 4-bytes long -- which is horribly insufficient) LESSON - Never skimp on buffer size!
To use the field-width modifier with scanf() when using "%s" you add the maximum number of bytes to read (not including the nul-terminating character), e.g. "199s" when writing to name. This ensures scanf() will not attempt to write more than 199 characters to name (plus the nul-terminating character)
All of this (and a lot more) are the pitfalls of scanf() and are why it is recommended to take all user input using fgets() or POSIX getline(). Provided a sufficiently sized buffer, the line-oriented input functions will consume an entire line of data. This eliminates all the problems inherent with scanf() and the only thing you need do is to trim the trailing '\n' read and included in the buffer they fill. This is trivial using strcspn(). It simply counts the number of initial characters that are not in its reject string -- providing an easy way to know how long the string is up to the '\n'. A better way to prompt, read/VALIDATE and trim the '\n' is:
fputs ("What are you looking for?: ", stdout); /* no conversion */
/* read/VALIDATE all input with fgets..
* it will consume a line at-a-time
* (given sufficient storage)
*/
if (!fgets (name, MAXNM, stdin)) {
return 0;
}
name[strcspn (name, "\n")] = 0; /* trim '\n' from end of name */
The other issue you have is using fflush(stdin);. The way you are using it is Undefined according to the C standard. fflush is not defined for input streams, see: C11 Standard - 7.21.5.2 The fflush function(p2) and fflush(stdin); leads to undefined behaviour on most systems.. It is not portable. For example, on Linux, fflush is only valid on seekable streams. Which only applies to stdin if a file is redirected as input (e.g. ./prog <somefile). See: fflush(3) - Linux manual page. Full discussion Standard C and POSIX leave fflush(stdin) as undefined behaviour
(MS is the only compiler I know of that provides a Non-Standard implementation that does clear stdin the way you are using it. -- Don't do that, it is virtually 100% non-portable to any other compiler)
I won't go over all the points detailed in the other good answers. But one additional note as you are learning to program. Always compile with warnings enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables). You can add -Werror to have all warnings treates as errors (recommended) For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. The warnings will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.
To put things altogether for you what I've done is just to change your implementation to use fgets() instead of scanf() while leaving your code commented out below so you can easily see exactly what changes are necessary. I have commented the code heavily so you can understand why the changes were made. I also changed the return type from void to int so you can provide a meaningful return indicating whether your function succeeded or failed. You will see how that benefits your code in main().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXINM 50 /* if you need a constant, #define one (or more) */
#define MAXNM 200
struct category {
char item_name[MAXINM];
int item_quantity;
float item_price;
};
/* choose a meaningful return type that can indicate whether
* the function succeeded or failed. (0 - not found, 1 - found)
*/
int items_search (struct category a[], int length)
{
char answer[MAXNM] = "yes", /* a string-literal initializer is fine */
name[MAXNM] = "",
tmp[MAXNM]; /* temporary array for input use */
int i = 0, /* group declarations by type */
// item_quantity = 0, /* UNUSED VARIABLE */
number = 0,
transaction = 0; /* flag indicating success/failure */
fputs ("What are you looking for?: ", stdout); /* no conversion */
/* read/VALIDATE all input with fgets..
* it will consume a line at-a-time
* (given sufficient storage)
*/
if (!fgets (name, MAXNM, stdin)) {
return 0;
}
// scanf ("%s", &name); /* scanf is full of pitfalls */
/* no & before name, it's a pointer */
name[strcspn (name, "\n")] = 0; /* trim '\n' from end of name */
for (; i < length; i++) { /* loop over each item in struct */
if (strcmp (a[i].item_name, name) == 0) { /* strcmp for equality */
fputs ("Desired number of the item?: ", stdout);
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
/* minimum validation of conversion with sscanf,
* for full error reporting, use strtol()
*/
if (sscanf (tmp, "%d", &number) != 1) {
fputs ("error: invalid integer input.\n", stderr);
return 0;
}
// scanf ("d", &number);
// fflush (stdin); /* defined for "seekable" streams */
if (number <= a[i].item_quantity) { /* less than or equal */
printf ("Order is possible.\n"
"Total Price: $%.2lf\n"
"Should the order be placed?: ",
number * a[i].item_price);
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
// scanf ("%s", &answer);
// fflush (stdin);
if (strcmp (answer, "yes") == 0) {
puts ("The order is placed");
}
else {
puts ("Order was cancelled");
}
}
else {
printf ("There are only %d pieces of the item.\nShould the "
"order be canceled?: ", a[i].item_quantity);
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
answer[strcspn (answer, "\n")] = 0; /* trim '\n' */
// scanf ("%s", &answer); /* answer is already a pointer */
// fflush (stdin);
if (strcmp (answer, "yes") == 0) { /* strcmp for equality */
puts ("Order was cancelled. Should be bought later?");
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
answer[strcspn (answer, "\n")] = 0; /* trim '\n' */
// scanf ("%s", &answer);
// fflush (stdin);
if (strcmp (answer, "yes") == 0) { /* strcmp */
puts ("\nThe order for later is placed");
}
else {
puts ("\nThe order was cancelled");
}
}
else {
puts ("The order is placed");
}
}
transaction = 1; /* set transaction complete flag */
}
// else { /* not needed here */
// printf ("The specified item does not exist\n");
// }
}
return transaction; /* return transaction status */
}
int main (void)
{
struct category laptops[] = {
{ "Asus_365", 7, 499.00 },
{ "Lenovo_L49", 30, 699.91 },
{ "HP_Alien", 20, 649.99 },
{ "Acer Alpha touch", 10, 899.99 }
};
if (!items_search (laptops, sizeof laptops / sizeof *laptops)) {
fputs ("error: incomplete transaction.\n", stderr);
}
}
Example Use/Output
$ ./bin/transaction
What are you looking for?: HP_Alien
Desired number of the item?: 2
Order is possible.
Total Price: $1299.98
Should the order be placed?: yes
The order is placed
or
$ ./bin/transaction
What are you looking for?: HP_Gorilla
error: incomplete transaction.
Look things over and let me know if you have any further questions. NOTE: this isn't the only way to approach this, just one way that improves over the UNVALIDATED use of scanf() and eliminates the potential for exploit through buffer overflow from the unlimited use of "%s".
Where to start...I feel like its the 80's again
buffer overruns = BAD
scanf does no validation
there are other issues with your code I leave that as an exercise for you to figure out the differences.
here is a more functional version
...
struct category {
const char* item_name; // const is good
int item_quantity;
double item_price; // float was too small
};
void items_search(category Category[])
{
char answer[4] = { "\0" }; // buffer was too small before
int number = 0;
int item_quantity = 0;
char name[200] = { "\0" };
int item = -1, Index = 0;;
printf("What are you looking for?\n");
scanf_s("%199s", &name, _countof(name)); // validation!
bool loop = true;
for (; nullptr != Category[Index].item_name && loop; Index++) {// does lower case comparison
if (_stricmp(Category[Index].item_name, name) == 0) {
item = Index;
loop = false;
}
}
if (-1 != item) { // did we find the item ?
printf("What is the desired number of the item?");
scanf_s("%d", &number);
fflush(stdin);
if (number <= Category[item].item_quantity) { // you only accepted exact answer
printf("Order is possible. Should the order be placed?\n");
scanf_s("%3s", &answer, _countof(answer));
fflush(stdin);
if (_stricmp(answer, "yes") == 0) {
puts("The order is placed");
}
else {
puts("Order was cancelled");
}
}
else {
printf("There are only %d pieces of the item. Should the order be canceled ? \n", Category[item].item_quantity);
scanf_s("%3s", &answer, _countof(answer));
fflush(stdin);
if (_stricmp(answer, "yes") == 0) {
puts("Order was cancelled. Should be bought later?");
scanf_s("%3s", &answer, _countof(answer));
fflush(stdin);
if (_stricmp(answer, "yes") == 0) {
puts("The order is placed");
}
else {
puts("The order was cancelled");
}
}
else {
puts("The order is placed");
}
}
} else {
printf("The specified item does not exist\n");
}
}
the rest should be clear
Also in main
struct category laptops[] = {
{"Asus_365", 7, 499.00},
{"Lenovo_L49", 30, 699.91},
{"HP_Alien", 20, 649.99},
{"Acer Alpha touch", 10, 899.99},
{nullptr,0,0 }
};
items_search(laptops); // see items_search above
Related
I am making a typedef to a structure for a "person" The person has a name, ssn and yearOfBirth. I am getting errors I do not understand with my for loops.
[Error] cannot convert 'person_t' to 'person_t*' for argument '1' to
'void getOnePerson(person_t*)'
This is the first file:
#include <stdio.h>
#include <string.h>
#include "structures.h"
void getOnePerson(person_t *p)
{
printf("Enter full name: ");
scanf("%99[^\n]", p -> name);
printf("Enter ssn: ");
scanf("%99[^\n]", p -> ssn);
printf("Enter year of birth: ");
scanf("%d", &p -> yearOfBirth);
}
void printOnePerson(person_t p)
{
printf("%s:", p.name);
printf("%s:", p.ssn);
printf("%s\n", p.yearOfBirth);
}
void getPeople(person_t p[], int numOfPeople)
{
for(int i = 0; i < sizeof(p); i++)
{
getOnePerson(p[i]);
}
}
void printPeople(person_t p[], int numOfPeople)
{
for(int i = 0; i < sizeof(p); i++)
{
printOnePerson(p[i]);
}
}
This is my structure file:
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct
{
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
First of all, it seems to be pointers and references task. You may need to read this to understand them. In other words, cannot convert person_t to person_t* means you are trying to use your object person instead of reference to that specific person. * means reference, so you need to pass an address to it using &. Im not best explainer, check out the link instead and all answers, not only accepted one.
Code seems quite messy, I tried to fix it to compilable code, although I dont have C compiler (you may need to edit/fix according to your homework details):
#include <stdio.h>
#include <string.h>
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct
{
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
int main()
{
person_t people[NUM_PEOPLE];
printf("Get people\n");
getPeople(&people, 3);
printf("\nPrint people\n");
printPeople(people, 3);
return 0;
}
void getOnePerson(person_t *person)
{
printf("Enter full name: ");
scanf("%s", person -> name);
printf("\nEnter ssn: ");
scanf("%s", person -> ssn);
printf("\nEnter year of birth: ");
scanf("%s", person -> yearOfBirth);
}
void printOnePerson(person_t p)
{
printf("%s:%s:%d\n", p.name, p.ssn, p.yearOfBirth);
}
void getPeople(person_t *person[], int num)
{
int i;
for(i=0; i<num; i++)
{
getOnePerson(&person[i]);
}
}
void printPeople(person_t person[], int num)
{
int i;
for(i=0; i<num; i++)
{
printOnePerson(person[i]);
}
}
So, briefly, your getPeople(person_t *person[], int num) function's first parameter is person_t *person[], therefore you need to pass a &people. Same as getOnePerson(person_t *person) parameter person_t *person means you need to pass address to a single person object &person[i]. The meaning behind them that using references, you can edit the values in these objects directly in the function. While printPeople(person_t person[], int num) and printOnePerson(person_t p) are used for reading (not editing) thereby you can pass values themselves.
You have such a large number of small problems, it is difficult to know where to begin. First a nit, you never include spaces around "->" when referencing a structure member. Use p->name, not p -> name. Continuing...
You fail to validate the return of scanf. You must check the return Every Time, or you are tempting Undefined Behavior. You also must change "%99[^\n]" to " %79[^\n]" because neither "%c" or "%[...]" consume leading whitespace. Failing to add the " " before %12[^\n] would make it impossible to read p->ssn and lead to a matching failure reading p->yearOfBirth.
Note the change from 99 to 79. You #define NAME_SIZE 80 and declare char name[NAME_SIZE];, what do you think you are doing using a field-width modifier of 99 when at most 79 characters can be stored in name? (You have the same problem with #define SSN_SIZE 13). You use the field-width modifier with scanf to protect your array bounds. Setting the *field-width modifier greater than your array size (-1) removes the protection it should provide altogether.
Your failure to check the return of scanf and handle the three cases of return necessary will lead to Undefined Behavior if the user accidentally makes a single error in input. Failure to check the return of scanf is one of the most common pitfall new C programmer fall into. It is mandatory for every user input. Otherwise, you can have no confidence your code is actually processing valid data.
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 canceled 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 (early versions));
(return < expected No. of conversions) a matching or input failure occurred. For a matching failure you must account for every character left in your input buffer. (scan forward in the input buffer reading and discarding characters until a '\n' or EOF is found); and finally
(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, within a needed range, etc..).
A short function implementation to empty all remaining characters in stdin in the event of matching failure could be as simple as:
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
(implementing in your code is left as an exercise for you)
Further, using type void as the return of an input function makes no sense. You must choose your return to provide the return of required information AND provide an indication of whether the input succeeded or failed. Using void for getOnePerson() means you have no way of knowing whether you received all valid input, or just received name, but not ssn, or if the user simply generated a manual EOF canceling input at each prompt. A simple integer return is all you need (e.g. return 0; on failure or return 1; only after all 3-inputs are validated) You could do something like:
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
/* validate each input for all 3 cases */
fputs ("\nEnter full name: ", stdout); /* no need for printf, no conversion */
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1; /* indicates all 3 input successfully received */
}
(note: input is complete when EOF is encountered, either manually generated by the user or encountered in the input stream)
void is also meaningless as a return for getPeople(). You can't use a for loop and just assume all inputs were successful, instead, you need to take input only while input is available, while protecting your array bounds, and then return the number of input actually received (which may be less than NUM_PEOPLE). Further, choose your type properly. For counters, size_t is the proper type (you can't have a negative number of persons), e.g.
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
When you pass an array as a parameter to a function, the array is converted to a pointer to the first element. So when you do sizeof(p) within a function -- that is not what you want and does not provide the number of elements in the array referenced by p -- what it does provide is sizeof(a_pointer), which is fixed by your compiler (e.g. 8-bytes on x86_64, 4-bytes on x86). You pass numOfPeople -- use it, e.g.
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
You will also want to fix printf("%s\n", p.yearOfBirth); (yearOfBirth is not a string...)
Your header is fine, but it is missing something. Always include header guards around the content of your header files to prevent multiple inclusions of the file, e.g.
#ifndef mystructures_h
#define mystructures_h 1
...
/* your header content */
...
#endif
(note: the 1 isn't required, but if you are defining a constant, it is never a bad idea to give it an affirmative value of your choosing)
There are probably more that were corrected, but those were the major points. Putting it altogether, you could do:
structures.h
#ifndef mystructures_h
#define mystructures_h 1
#include <stdio.h>
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct {
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
size_t getPeople (person_t *p, size_t numOfPeople);
void printPeople (person_t *p, size_t numOfPeople);
#endif
(can you figure out why #include <stdio.h> was moved from structures.c into structures.h? do you know why the function prototypes for getPeople() and printPeople() are required in the header and not the rest?)
structures.c
#include "structures.h"
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
fputs ("\nEnter full name: ", stdout);
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1;
}
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
void printOnePerson (person_t p)
{
printf("%s:", p.name);
printf("%s:", p.ssn);
// printf("%s\n", p.yearOfBirth);
printf("%d\n", p.yearOfBirth);
}
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
A short test program peopletest.c
#include "structures.h"
int main (void) {
person_t people[NUM_PEOPLE] = {{ .name = "" }};
size_t npeople = getPeople (people, NUM_PEOPLE);
printPeople (people, npeople);
}
Example Use/Output
$ ./bin/peopletest
Enter full name: Person A. One
Enter ssn: 123456789
Enter year of birth: 2001
Enter full name: Person B. Two
Enter ssn: 234567890
Enter year of birth: 2002
Enter full name: Person C. Three
Enter ssn: 345678901
Enter year of birth: 2003
Enter full name: (input complete)
Stored People
Person A. One:123456789:2001
Person B. Two:234567890:2002
Person C. Three:345678901:2003
Look things over and let me know if you have further questions.
This is a short excerpt from a bigger program, but the rest of the program is irrelevant since I think I was able to isolate the issue. I suspect that it has something to do with the way I'm using fgets. I've read that it's preferable to use fgets over scanf, but I can't seem to get it to work properly here. When I use the following code, the program doesn't give me a chance to enter the number (but simply skips to the while loop which checks if the number entered is in the correct range):
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
int main(void)
{
// ask user for how many items to store
printf("how many words would you like to enter? (1-%i): ", SIZE);
// save number of words user would like to store
char *input = malloc(sizeof(char));
fgets(input, 1, stdin);
// scanf("%c", input);
int words = atoi(input);
printf("the number of words is: %i\n", words);
while (words < 1 || words > SIZE)
{
printf("please enter a number between 1 and %i: ", SIZE);
scanf("%i", &words);
}
}
Here's the output I get:
~/workspace/extra_stuff/hash_tables/ $ ./test2
how many words would you like to enter? (1-10): the number of words is: 0
please enter a number between 1 and 10:
As you can see, it never let me enter the number, but simply moved on to the next step seemingly assuming I didn't enter anything.
If I change the code as follows, everything works as planned:
#include <stdlib.h>
#define SIZE 10
int main(void)
{
// ask user for how many items to store
printf("how many words would you like to enter? (1-%i): ", SIZE);
// save number of words user would like to store
char *input = malloc(sizeof(char));
// fgets(input, 1, stdin);
scanf("%c", input);
int words = atoi(input);
printf("the number of words is: %i\n", words);
while (words < 1 || words > SIZE)
{
printf("please enter a number between 1 and %i: ", SIZE);
scanf("%i", &words);
}
}
P.S.: I do realize that if using scanf, I could immediately store the input to an int variable w/o using atoi to convert char to int; however it seems that fgets requires a char *, so that's why I chose this route. Also, I realize that I'm supposed to free(input) later.
Can someone explain this behavior? Thanks.
EDIT:
Thanks to everyone who has replied so far! Some helpful suggestions there, but it looks like I'm having the same issue further in my program. Here's the code excerpt:
// ask for strings
for (int j = 0; j < words; j++)
{
char buffer[4096];
// fgets(buffer, 40, stdin);
// name=calloc(NAME_SIZE, sizeof(char));
// fgets(name, NAME_SIZE, stdin);
// printf("size of (array[j]->next)->text is: %lu\n", sizeof((array[j]->next)->text));
printf("please enter string #%i: ", j);
fgets(buffer, 4096, stdin);
printf("you've entered: %s", buffer);
int length = strlen(buffer);
printf("word length: %i\n", length);
}
When I run the program, it once again doesn't give me a chance to enter my input when it's supposed to:
please enter string #0: you've entered:
word length: 1
EDIT #2:
After working through David's answer and referencing other people's comments and other SO threads, I've come up with the following version of the code, which first asks the user for the number of words they'd like to enter (and validates the input) and then asks the user to enter those words (again, validating the input). It seems to be compiling w/o errors and warnings and functioning properly, though I am not 100% sure I've tested all the possible things that could go wrong with the user input, and there are some bits of the code I still don't completely understand (I'll list them below) -- if anyone has time/desire/patience to look through it and tell me if I can still improve something, please let me know. My goal is to use this code in another program that will ask for user input and store the entries in a hash table.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE_WORDS 4096
#define BUF_SIZE_NUMBERS 256
#define MAX_WORDS 10
int word_input(int num_words);
void empty_stdin();
int main(void)
{
int num_words = 0, /* number of words to enter */
word_count_check = 0; /* word count */
char buffer[BUF_SIZE_NUMBERS] = ""; /* buffer of sufficient size for input */
for (;;) /* loop continually until valid input of NUMBER OF WORDS USER WANTS TO ENTER or user cancels */
{
printf ("how many words would you like to enter? [1-%d]: ", MAX_WORDS);
// check for cancellation of input
if (!fgets (buffer, BUF_SIZE_NUMBERS, stdin))
{
fputs ("user canceled input\n", stderr);
return 1;
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a value\n");
continue;
}
size_t inlength = strlen(buffer);
// validate length < BUF_SIZE_NUMBERS - 1
if (inlength >= BUF_SIZE_NUMBERS - 1)
{
fputs ("input exceeds allocated buffer size\n", stderr);
return 2;
}
if (inlength && buffer[inlength - 1] == '\n')
{
// printf("hurray!\n");
buffer[--inlength] = 0;
}
else if (inlength == BUF_SIZE_NUMBERS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_NUMBERS);
empty_stdin();
continue;
}
// make sure user actually entered a proper int
if (sscanf (buffer, "%d", &num_words) != 1) /* sscanf is used for conversion */
{
fputs ("invalid conversion to int; please provide valid input\n", stderr);
continue;
}
// check if the number entered is out of range
if (num_words < 1 || num_words > MAX_WORDS)
fprintf (stderr, "%2d out of valid range.\n", num_words);
else
break; /*if the input has been validated, we can now break out of the for loop */
}
// call the word_input function and store its return value in word_count_check
word_count_check = word_input(num_words);
// check if the number of words processed equals to the number requested by the user
if(word_count_check == num_words)
{
printf("success!\n");
}
else
{
printf("something went wrong, since word_count_check != num_words...\n");
}
}
int word_input(int num_words)
{
int word_count = 0;
for(;;) /* loop until word_count == num_words is achieved */
{
// declare an array for storing input string
char buffer[BUF_SIZE_WORDS];
char valid_input[BUF_SIZE_WORDS];
// prompt user for input
printf("please enter a string: ");
// get input and check for CTRL+D
if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
{
fputs ("user canceled input\n", stderr);
exit(1);
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a word that's more than 0 characters\n");
// empty_stdin();
continue;
}
size_t inlength = strlen(buffer);
// check if user input exceed buffer size
if (inlength >= BUF_SIZE_WORDS - 1)
{
empty_stdin();
fputs ("input exceeds allocated buffer size, please try again\n", stderr);
continue;
}
// check if the user entered too many characters
if (inlength == BUF_SIZE_WORDS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_WORDS);
empty_stdin();
continue;
}
if (inlength && buffer[inlength - 1] == '\n')
{
buffer[--inlength] = 0;
// get rid of trailing spaces using sscanf
sscanf(buffer, "%s", valid_input);
// figure out the length of the word the user entered
int word_length = ((int) strlen(valid_input));
printf("string length: %i\n", word_length);
// print out the word entered by the user one character at a time
printf("you've entered: ");
for (int i = 0; i < word_length; i++)
{
printf("%c", valid_input[i]);
}
printf("\n");
// increment word count
word_count++;
printf("word_count = %i\n", word_count);
if (word_count == num_words)
{
return word_count;
}
}
}
}
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
things I don't completely understand yet:
1)
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
--- does this simply check if the user manually entered EOF (with ctrl+d), or does it check for something else too?
2) calling the empty_stdin() function below seemed to be causing some kind of a weird hang-up where it looked like the program was expecting further input from me as opposed to just going on to the next step, especially when I used it frequently (I figured why not clear the input stream every time the user types in something weird?) and/or when I decreased the buffer to something very small and then purposefully entered too many characters..
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
3) eventually I want to use some of this code to load a dictionary from a text file (instead of user input) and store it in a hash table, and, in another version, in a trie. Besides using isalpha() to make sure we're only storing words that have letters in them, are there any other checks/validations that need to happen when processing the input, beside the ones above? Should any of the above checks be skipped?
There is no magic involved in dealing with strings in C -- but you do need to put your accounting hat on... Why? When dealing with input, you have to not only account for the number of characters that you place into your buffer (or wherever you are storing your input), but you also must account for the characters that remain in your input stream!
This is particularly true when using any of the scanf family of function for input. Why? Because on a matching or input failure, processing (reading and removing) characters from your input buffer (stdin here) stops, no further characters are read, and any character causing the matching failure remains unread in your input stream, just waiting to bite you again on your next attempted read.
Compounding this bewilderment for new C programmers is the fact that some conversion specifiers consume leading whitespace (e.g. space, tab, newline,...) and others do not. Your numeric conversion specifiers (along with "%s") consume leading whitespace while "%c" and "%[...]" do not.
All of which are the primary reasons new C programmers are encouraged to use line-oriented input functions like fgets or POSIX getline to handle user input (because they read the entire line at a time -- including the trialing '\n') freeing the new programmer for having to account for ending whitespace or offending characters not converted in the event of a matching failure...
Using fgets followed by a sscanf provides the additional benefit of allowing separate validation of (1) the read of input; and (2) the parse and conversion of input into the needed values.
(note: the only caveat with line-oriented input functions is that they read and include the trailing '\n' in the buffer they fill -- so you will need to "trim" the trailing whitespace as required. You don't want stray '\n' characters dangling off the end of the strings you are storing.)
That said, there will be times when reading input with the scanf family of functions makes sense. There is nothing wrong with doing so, so long as you validate the return every time and handle all three possible conditions:
the user presses ctrl+d on Linux to generate a manual EOF (ctrl+z on windoze);
you handle the matching or input failure cases, including removing any offending characters from your input buffer before your next attempted read; and finally
you have good input (the return indicates all the conversions anticipated, took place).
There is no magic to any of it, but it does take understanding the possible error conditions and handling each of them on every input.
In your case, let's look at your task of getting the number of words to enter from the user. Here you were attempting to read with fgets (that's good!), but you failed to provide sufficient storage to hold the input. When reading small amount of text from the user, a simple array with automatic storage type is all you need. However, you need to size the buffer accordingly (and do NOT skimp on buffer size).
There is no golden rule, but if I had users entering text to convert to a single number, then I would feel good with a 256 character buffer (which provides more than enough to hold the input of any valid number, plus another 230 some-odd characters to handle the time the cat steps on the keyboard, etc..)
For example, taking input from the user and getting the number of words to enter could be done in a manner similar to the following:
#include <stdio.h>
#define SIZE 10 /* good form defining a constant! */
#define MAXC 256 /* max characters for buffer */
int main (void) {
int nwords = 0, /* number of words to enter */
words = 0, /* each word */
wc = 0; /* word count */
char buf[MAXC] = ""; /* buffer of sufficient size for input */
for (;;) { /* loop continually until valid input or user cancels */
printf ("number of words to enter? [1-%d]: ", SIZE);
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* validate length < MAXC - 1 and buf[length-1] == '\n' here */
if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
fputs (" error: invalid conversion to int.\n", stderr);
continue;
}
if (nwords < 1 || SIZE < nwords) /* validate nwords in range */
fprintf (stderr, " %2d out of valid range.\n", nwords);
else /* good input received, break loop */
break;
}
(note: your while loop has been converted to a loop that will loop continually until valid input between 1 < value < SIZE is entered. The condition simply causes control to break; the loop at the point good input is received)
This loop presents a classic fgets/sscanf read and parse of information from the line of input entered by the user. You can parse the number from the line any way you like (but don't use atoi() -- it provides absolutely zero error checking of the conversion). You can use strtol (with proper validation) and you can simply use a pointer to walk-down-the-buffer, picking out digits, converting them from their ASCII to numeric value and then multiplying by 10 and adding as your go. Any way is fine so long as you validate, validate, validate each part of the operation.
Now turning to reading each of the words the user is supposed to enter, we will ignore conventional wisdom and use scanf for the task, but we will handle all three possible cases of the return every time. We will also add a counter to keep track of the valid inputs provided by the user and only exit the loop when we have that number of valid integers provided (or the user cancels by generating a manual EOF).
printf ("\nnumber of words entered: %d\n", nwords);
for (; wc < nwords;) { /* loop continually */
int rtn = 0; /* scanf return */
printf ("please enter a number between 1 and %d: ", SIZE);
rtn = scanf ("%d", &words); /* valdate ALL user input */
if (rtn == EOF) { /* handle EOF (manual) */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn == 0) { /* handle "matching failure" */
int c = getchar(); /* remove offending chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
fputs (" error: invalid integer input\n", stderr);
continue;
}
else { /* valid integer received */
int c = getchar(); /* remove any extra chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
if (words < 1 || SIZE < words) /* validate in-range */
fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n",
words, SIZE);
else /* good input, increment word count */
printf (" word[%2d]: %3d\n", ++wc, words);
}
}
Note: the emptying of any offending characters from stdin can be turned into a convenient function so that you do not have to duplicate the loops each time you need to clear stdin during your input routine. You can replace it with a simple function, e.g.
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
That will help keep your code tidy. I'll let you incorporate that above.
Putting it altogether, you could do something like the following:
#include <stdio.h>
#define SIZE 10 /* good form defining a constant! */
#define MAXC 256 /* max characters for buffer */
int main (void) {
int nwords = 0, /* number of words to enter */
words = 0, /* each word */
wc = 0; /* word count */
char buf[MAXC] = ""; /* buffer of sufficient size for input */
for (;;) { /* loop continually until valid input or user cancels */
printf ("number of words to enter? [1-%d]: ", SIZE);
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* validate length < MAXC - 1 and buf[length-1] == '\n' here */
if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
fputs (" error: invalid conversion to int.\n", stderr);
continue;
}
if (nwords < 1 || SIZE < nwords)
fprintf (stderr, " %2d out of valid range.\n", nwords);
else
break;
}
printf ("\nnumber of words entered: %d\n", nwords);
for (; wc < nwords;) { /* loop continually */
int rtn = 0; /* scanf return */
printf ("please enter a number between 1 and %d: ", SIZE);
rtn = scanf ("%d", &words); /* valdate ALL user input */
if (rtn == EOF) { /* handle EOF (manual) */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn == 0) { /* handle "matching failure" */
int c = getchar(); /* remove offending chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
fputs (" error: invalid integer input\n", stderr);
continue;
}
else { /* valid integer received */
int c = getchar(); /* remove any extra chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
if (words < 1 || SIZE < words) /* validate in-range */
fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n",
words, SIZE);
else /* good input, increment word count */
printf (" word[%2d]: %3d\n", ++wc, words);
}
}
}
Example Use/Output
$ ./bin/getintstdin
number of words to enter? [1-10]: five, maybe six?
error: invalid conversion to int.
number of words to enter? [1-10]: -2
-2 out of valid range.
number of words to enter? [1-10]: 3
number of words entered: 3
please enter a number between 1 and 10: two? three?
error: invalid integer input
please enter a number between 1 and 10: 2
word[ 1]: 2
please enter a number between 1 and 10: -2
-2 - invalid! (1 < valid < 10)
please enter a number between 1 and 10: 11
11 - invalid! (1 < valid < 10)
please enter a number between 1 and 10: 3
word[ 2]: 3
please enter a number between 1 and 10: 4
word[ 3]: 4
Note all the invalid inputs above and how the code handles each. So long as the input does not exceed 255-characters with fgets, the code will gracefully respond to inputs that are not valid integers (regardless how many are given) and it will respond to integer inputs that are out-of-range.
The code isn't much longer than the code you posted, but it addresses the possible error conditions that could arise and then handles the errors. When you boil it all down, that is what coding is all about. Look things over and let me know if you have further questions.
I have created a code reverse a string but for some reason it is not working. But I think my logic is right. Then why is it not working??
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
printf("\nThe Reverse is: ");
while (i<=0) {
printf("%s",words[i]);
i--;
}
return 0;
}
While you already have an answer, there are a few additional points you need to consider before you have a solution that doesn't have the potential to invoke Undefined behavior.
First, always, always validate all user input. For all you know a cat could have gone to sleep on the 'L' key (with millions being entered), or a more likely case, the user just decides to type a 100-char sentence (or more) which leaves 'words' as an array of chars that is NOT nul-terminated and thus not a valid string in C. Your loop to get the length now invokes Undefined Behavior by reading beyond the end of words off into the stack until the first random '0' is encounter or a SegFault occurs.
To prevent this behavior (you should really just use fgets) but with scanf you can provide a field-width modifier to prevent reading more than length - 1 chars. This insures space for the nul-terminating character.
Further, the "%s" conversion-specifier stops conversion on the first whitespace character encountered -- making your "Enter a .../sentence" an impossibility because scanf ("%s", words) will stop reading after the first word (at the first whitespace.
To correct this problem (you should really just use fgets) or with scanf you can use a character class (stuff between [...]) as the conversion specifier that will read until a '\n' is encountered., e.g. scanf ("%[^\n]", words). However, recall, that is still not good enough because more than 99-chars can be entered leaving the string un-terminated at 100 and invoking Undefined Behavior at character 101 (off the end of the array).
To prevent this problem (ditto on fgets), or include the field-width modifier, e.g. scanf ("%99[^\n]", words). Now no more than 99-chars will be read regardless of the cat sleeping on the 'L' key.
Putting that altogether, you could do something like:
#include <stdio.h>
#define MAXC 100 /* if you need a constant, define one */
int main(void) {
char words[MAXC] = "";
int i = 0, rtn = 0; /* rtn - capture the return of scanf */
printf ("Enter a word/sentence : ");
if ((rtn = scanf ("%99[^\n]", words)) != 1) { /* validate ! */
if (rtn == EOF) /* user cancel? [ctrl+d] or [ctrl+z]? */
fprintf (stderr, "user input canceled.\n");
else /* did an input failure occur ? */
fprintf (stderr, "error: invalid input - input failure.\n");
return 1; /* either way, bail */
}
for (; words[i]; i++) {} /* get the length */
printf ("Reversed word/sentence: ");
while (i--)
putchar (words[i]); /* no need for printf to output 1-char */
putchar ('\n');
return 0;
}
Example Use/Output
$ ./bin/strrevloops
Enter a word/sentence : My dog has fleas.
Reversed word/sentence: .saelf sah god yM
Look things over and let me know if you have any further questions.
There are few mistakes in your program.
After you have reached the end of the string.You should do i-- as your array index of i will be pointing to '\0'.
Your while loop checks for <= but it should be >=.
Use %c for printing chararcters. %s is used to print strings and not char.
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
i--;
printf("\nThe Reverse is: ");
while (i>=0) {
printf("%c",words[i]);
i--;
}
return 0;
}
Given the following code:
#include <stdio.h>
int main()
{
int testcase;
char arr[30];
int f,F,m;
scanf("%d",&testcase);
while(testcase--)
{
printf("Enter the string\n");
fgets(arr,20,stdin);
printf("Enter a character\n");
F=getchar();
while((f=getchar())!=EOF && f!='\n')
;
putchar(F);
printf("\n");
printf("Enter a number\n");
scanf("%d",&m);
}
return 0;
}
I want a user to enter a string, a character and a number until the testcase becomes zero.
My doubts / questions:
1.User is unable to enter a string. It seems fgets is not working. Why?
2.If i use scanf instead of fgets,then getchar is not working properly, i.e whatever character I input in it just putchar as a new line. Why?
Thanks for the help.
Mixing functions like fgets(), scanf(), and getchar() is error-prone. The scanf() function usually leaves a \n character behind in the input stream, while fgets() usually does not, meaning that the next call to an I/O function may or may not need to cope with what the previous call has left in the input stream.
A better solution is to use one style of I/O function for all user input. fgets() used in conjunction with sscanf() works well for this. Return values from functions should be checked, and fgets() returns a null pointer in the event of an error; sscanf() returns the number of successful assignments made, which can be used to validate that input is as expected.
Here is a modified version of the posted code. fgets() stores input in a generously allocated buffer; note that this function stores input up to and including the \n character if there is enough room. If the input string is not expected to contain spaces, sscanf() can be used to extract the string, leaving no need to worry about the newline character; similarly, using sscanf() to extract character or numeric input relieves code of the burden of further handling of the \n.
#include <stdio.h>
int main(void)
{
int testcase;
char arr[30];
char F;
int m;
char buffer[1000];
do {
puts("Enter number of test cases:");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
} while (sscanf(buffer, "%d", &testcase) != 1 || testcase < 0);
while(testcase--)
{
puts("Enter the string");
/* if string should not contain spaces... */
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
sscanf(buffer, "%29s", arr);
printf("You entered: %s\n", arr);
putchar('\n');
puts("Enter a character");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
sscanf(buffer, "%c", &F);
printf("You entered: %c\n", F);
putchar('\n');
do {
puts("Enter a number");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
} while (sscanf(buffer, "%d", &m) != 1);
printf("You entered: %d\n", m);
putchar('\n');
}
return 0;
}
On the other hand, if the input string may contain spaces, fgets() can read input directly into arr, but then the stored string will contain a \n character, which should probably be removed. One way of doing this is to use the strcspn() function to find the index of the \n:
#include <string.h> // for strcspn()
/* ... */
puts("Enter the string");
/* or, if string may contain spaces */
if (fgets(arr, sizeof arr, stdin) == NULL) {
/* handle error */
}
/* replace newline */
arr[strcspn(arr, "\r\n")] = '\0';
printf("You entered: %s\n", arr);
putchar('\n');
/* ... */
Note that a maximum width should always be specified when using %s with the scanf() functions to avoid buffer overflow. Here, it is %29s when reading into arr, since arr can hold 30 chars, and space must be reserved for the null terminator (\0). Return values from sscanf() are checked to see if user input is invalid, in which case the input is asked for again. If the number of test cases is less than 0, input must be entered again.
Finally got the solution how can we use scanf and fgets together safely.
#include <stdio.h>
int main()
{
int testcase,f,F,m;
char arr[30];
scanf("%d",&testcase);
while((f=getchar())!=EOF && f!='\n')
;
while(testcase--)
{
printf("Enter the string\n");
fgets(arr,30,stdin);
printf("Enter a character\n");
F=getchar();
while((f=getchar())!=EOF && f!='\n')
;
putchar(F);
printf("\n");
printf("Enter a number\n");
scanf("%d",&m);
while((f=getchar())!=EOF && f!='\n')
;
}
}
We need to make sure that before fgets read anything,flushout the buffer with simple while loop.
Thanks to all for the help.
A simple hack is to write a function to interpret the newline character. Call clear() after each scanf's
void clear (void){
int c = 0;
while ((c = getchar()) != '\n' && c != EOF);
}
Refer to this question for further explaination: C: Multiple scanf's, when I enter in a value for one scanf it skips the second scanf
This question already has an answer here:
How to read / parse input in C? The FAQ
(1 answer)
Closed 6 years ago.
I am trying to write a simple program which will read two input lines, an integer followed by a string. However, it doesn't seem to work for me.
int main()
{
int i;
char str[1024];
scanf("%d", &i);
scanf("%[^\n]", str);
printf("%d\n", i);
printf("%s\n", str);
return 0;
}
Immediately after entering the integer and pressing "Enter", the program prints the integer. It doesn't wait for me to enter the string. Whats wrong? Whats the correct way to program this?
What you need to know
The problem with %[^\n] is that it fails when the first character to be read is the newline character, and pushes it back into the stdin.
The Problem
After you enter a number for the first scanf, you press Enter. %d in the first scanf consumes the number, leaving the newline character ('\n'), generated by the Enter keypress, in the standard input stream (stdin). %[^\n] in the next scanf sees this \n and fails for the reason given in the first paragraph of this answer.
Fixes
Solutions include:
Changing scanf("%d", &i); to scanf("%d%*c", &i);. What %*c does is, it scans and discards a character.
I wouldn't recommend this way because an evil user could trick the scanf by inputting something like <number><a character>\n, ex: 2j\n and you'll face the same problem again.
Adding a space (any whitespace character will do) before %[^\n], i.e, changing scanf("%[^\n]", str); to scanf(" %[^\n]", str); as #Bathsheba mentioned in a comment.
What the whitespace character does is, it scans and discards any number of whitespace characters, including none, until the first non-whitespace character.
This means that any leading whitespace characters will be skipped when inputting for the second scanf.
This is my recommendation: Clear the stdin after every scanf. Create a function:
void flushstdin(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF);
}
and call it after every scanf using flushstdin();.
Other issues:
Issues unrelated to your problem include:
You don't deal with the case if scanf fails. This can be due to a variety of reasons, say, malformed input, like inputting an alphabet for %d.
To do this, check the return value of scanf. It returns the number of items successfully scanned and assigned or -1 if EOF was encountered.
You don't check for buffer overflows. You need to prevent scanning in more than 1023 characters (+1 for the NUL-terminator) into str.
This can be acheived by using a length specifier in scanf.
The standards require main to be declared using either int main(void) or int main(int argc, char* argv[]), not int main().
You forgot to include stdio.h (for printf and scanf)
Fixed, Complete Program
#include <stdio.h>
void flushstdin(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF);
}
int main(void)
{
int i;
char str[1024];
int retVal;
while((retVal = scanf("%d", &i)) != 1)
{
if(retVal == 0)
{
fputs("Invalid input; Try again", stderr);
flushstdin();
}
else
{
fputs("EOF detected; Bailing out!", stderr);
return -1;
}
}
flushstdin();
while((retVal = scanf("%1023[^\n]", str)) != 1)
{
if(retVal == 0)
{
fputs("Empty input; Try again", stderr);
flushstdin();
}
else
{
fputs("EOF detected; Bailing out!", stderr);
return -1;
}
}
flushstdin();
printf("%d\n", i);
printf("%s\n", str);
return 0;
}
This simply, will work:
scanf("%d %[^\n]s", &i, str);
Instaed of scanf() use fgets() followed by sscanf().
Check return values of almost all functions with a prototype in <stdio.h>.
#include <stdio.h>
int main(void) {
int i;
char test[1024]; // I try to avoid identifiers starting with "str"
char tmp[10000]; // input buffer
// first line
if (fgets(tmp, sizeof tmp, stdin)) {
if (sscanf(tmp, "%d", &i) != 1) {
/* conversion error */;
}
} else {
/* input error */;
}
// second line: read directly into test
if (fgets(test, sizeof test, stdin)) {
size_t len = strlen(test);
if (test[len - 1] == '\n') test[--len] = 0; // remove trailing ENTER
// use i and test
printf("i is %d\n", i);
printf("test is \"%s\" (len: %d)\n", test, (int)len);
} else {
/* input error */;
}
return 0;
}