I made a program to take multiple inputs as string, store and print them.
Somehow the "Account Number" that's stored in a[i].acn is not printing. I've tried debugging and the problem seems to be in the loop that adds spaces to a[i].name .
#include <stdio.h>
#include <string.h>
struct Bank
{
char name[10],acn[8];
float bal;
}a[100];
int n,i,flag;
void add()
{
//function to add Rs. 100 to accounts that have balance over 1000
//and print the details.
for(i=0;i<n;i++)
if(a[i].bal>=1000)
a[i].bal+=100;
printf("\nS.No.\tName\t\tAcc. No.\tBalance(in Rs.)");
for(i=0;i<n;i++)
printf("\n[%d]\t%s\t%s\t%.2f",i+1,a[i].name,a[i].acn,a[i].bal);
}
void main()
{
printf("Enter the number of customers: ");
scanf("%d",&n);
printf("Enter the details of %d customers:\n",n);
for(i=0;i<n;i++)
{
printf("\nCustomer-%d",i+1);
printf("\nFirst Name: ");
fflush(stdin);
gets(a[i].name);
printf("Account Number: ");
fflush(stdin);
gets(a[i].acn);
printf("Account Balance: ");
scanf("%f",&a[i].bal);
}
for(i=0;i<n;i++)//The problem seems to be in this loop
while(strlen(a[i].name)<10)
strcat(a[i].name," ");
add();
}
Input:
Enter the number of customers: 2
Enter the details of 2 customers:
Customer-1
First Name: Aarav
Account Number: ASDF1234
Account Balance: 1200
Customer-2
First Name: Asd
Account Number: abcd1122
Account Balance: 999.9
Output:
S.No. Name Acc. No. Balance(in Rs.)
[1] Aarav 1300.00
[2] Asd 999.90
scanf() then gets() is bad
scanf("%d",&n); reads the integer, yet leaves the following '\n' in stdin that gets() reads as an empty string "".
I recommend to use fgets() to read all the line into a sizable buffer and then parse using sscanf(), strtol() etc.
Code overfills buffer
a[i].nam only has enough room for 9 characters and then the null character. Appending a space until it has a length more the 9 overfills .name[10].
struct Bank {
char name[10],acn[8];
float bal;
} a[100];
while(strlen(a[i].name)<10)
strcat(a[i].name," ");
Use while(strlen(a[i].name)<9) to pad with spaces, but not too many.
Money needs special considerations. For now, consider a long of lowest denominational (cents). Read in a double and then long balance = lround(input*100.0);
Other weaknesses exist.
There are few things to correct in your program.
Use fgets instead of gets because in gets there is no boundary check and there is a danger of reading beyond the allocated size.
When using fgets , scanf and even gets they all read the left over \n characters from the standard buffer , so using fgets we can avoid it if we properly use it.(scanf also avoids space characters but it cannot be used to read multiword strings)
remove fflush(stdin), its not required you can see reason here
And last, but certainly not least use int main() instead of void main()
You have this:
struct Bank
{
char name[10],acn[8];
float bal;
}a[100];
since C strings must end with a NUL character (aka '\0') the name can be at maximum 9 chars long.
But here
while(strlen(a[i].name)<10)
strcat(a[i].name," ");
you keep adding spaces until it's 10 characters long. In other word - you write outside the array.
Change name to be name[11]
Besides that gets and fflush(stdin) should be avoided.
Related
I have simple program, in which i am taking information and forming record out of them.
if i give the following input:
name: rahul
usn: 1rv16is038
age:20
the strcat() is missing name string. output should be 1rv16is038|rahul|20|########## but instead output is 1rv16is038||20|###############.
why this unusual behavior?
#include<stdio.h>
#include<string.h>
#define LEN 30
int main(){
char record[255],name[30],usn[10],age[5];
int index;
printf("\nEnter name: ");
scanf(" %s",name);
printf("\nEnter usn: ");
scanf(" %s",usn);
printf("\nEnter age: ");
scanf(" %s",age);
strcpy(record,usn);
strcat(record,"|");
strcat(record,name);
strcat(record,"|");
strcat(record,age);
strcat(record,"|");
for(int i=strlen(record);i<LEN;i++){
strcat(record,"#");
}
printf("\nRecord created is: %s",record);
return 0;
}
I have checked that all the variable is taking input, the problem lies in `strcat()'
1rv16is038 is 10 characters, so you need 11 chars to store it plus the null terminator. The usn array is only 10 chars, so reading this into it is undefined behavior.
In general, you should avoid scanf("%s", ...) because there is no way to prevent buffer overflow. You can use fgets instead if you are reading separate lines, or limit the size with e.g. scanf("%9s", ...).
If I try to run this code then it doesn't ask me the value of s2.name. Why is it so?
#include<stdio.h>
int main()
{
struct student
{
char name;
int roll;
int age;
};
struct student s1;
struct student s2;
printf("Enter name of the student: ");
scanf("%c", &s1.name);
printf("%c", s1.name);
printf("\n");
printf("Enter name of the student: ");
scanf("%c", &s2.name);
printf("%c", s2.name);
return 0;
}
When you input a single character and press the Enter key, you are actually inputting two characters: The character in your input and a newline from the Enter key.
The second scanf reads this newline.
Or if you give multiple characters as input to the first name, then the second character will be read by the second scanf.
The way to solve the first problem is easy: Tell scanf to read and discard leading white-space (which newline is) by adding a single space in front of the format, like
scanf(" %c", &s2.name);
// ^
// Note space here
The way to solve the second problem is to read strings instead, which means you have to turn your name members into arrays and then use the "%s" format (preferably with a specified width so you don't read to many characters).
You are basically inputting two characters:
- the one that you type
- the newline character `\n` because you hit `enter`
A solution to this problem is clearing stdin after reading in the first "name":
#include<stdio.h>
int main()
{
struct student
{
char name;
int roll;
int age;
};
struct student s1;
struct student s2;
printf("Enter name of the student: ");
scanf("%c", &s1.name);
printf("%c", s1.name);
printf("\n");
fflush(stdin); //only works on windows, clears the input buffer
printf("Enter name of the student: ");
scanf("%c", &s2.name);
printf("%c", s2.name);
return 0;
}
Another way to clear the input buffer is:
while (getchar() != '\n');
This reads in all characters from the input buffer, because as soon as input is read by functions like getchar() or scanf(), the input is removed from stdin.
EDIT:
When you input values using getchar(), scanf(), etc., then the symbols that you type into stdin (most of the times via a keyboard) are stored in the input buffer at first (stdin). getchar() or any similar function then takes the values that it should read in, out of the input buffer. For example:
scanf("%d", &var);
scanf("%d", &var2);
If I input 5x, the characters in the input buffer are '5', 'x' and \n (because you hit the enter key). The first scanf() then takes out the '5', as it fits the format string %d. After that, the characters in the input buffer are 'x' and \n. In this case scanf returns 1, because it read in one value correctly.
When it then continues to the second one, scanf() won't even let you type anything, as there is already something in the input buffer stdin. It won't store any data however, because the first "item" in stdin ist 'x'. That doesn't fit the format string %d. The compiler then doesn't continue to read. In this case scanf would return 0, as no value was read in correctly.
The function computeScore() is used to create a database of max 50 students using an array of structures. The function takes in the student, name, test score and exam score, compute total and then print it out. Input will end when student name is 'END'. After which, the program will compute the total average score or all the students and prints it.
It works fine for when I keey in the first student's info, but I'm having trouble when the program enters the while loop the second time (when I want to key in the second student's info).
Here's what I did so far:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <math.h>
struct student{
char name[20]; /* student name */
double testScore; /* test score */
double examScore; /* exam score */
double total; /* total score = test+exam scores */
};
void computeScore();
int main()
{
computeScore();
return 0;
}
void computeScore()
{
struct student info[50];
int count = 1;
double allStudent = 0, allStudentAvg;
while (info[count].name != 'END')
{
printf("\nEnter student name: ");
gets(info[count].name);
printf("Enter test score: ");
scanf("%lf", &info[count].testScore);
printf("Enter exam score: ");
scanf("%lf", &info[count].examScore);
info[count].total = (info[count].testScore + info[count].examScore) / 2;
printf("Student %s's total= %f\n", info[count].name, info[count].total);
allStudent += info[count].total;
count++;
}
allStudentAvg = allStudent / count;
printf("Overall average = %f", allStudentAvg);
}
Expected output:
Enter student name: John Doe
Enter test score: 34
Enter exam score: 46
Student John Doe's total = 40.000000
Enter student name: Jane Doe
Enter test score: 60
Enter exam score: 80
Student John Doe's total = 70.000000
Enter student name: END
Overall average: 55.000000
Output I got instead:
Enter student name: John Doe
Enter test score: 34
Enter exam score: 46
Student John Doe's total = 40.000000
Enter student name: Enter test score:
\\Program skipped the input of 2nd student name
It's because of the last call to scanf, it leaves the newline still in the buffer so the next gets reads that newline.
You can explicitly get that newline with a dummy gets call, but it's easier to tell scanf to simply read (and discard) trailing whitespace by adding a space after the format:
scanf("%lf ", &info[count].examScore);
/* ^ */
/* | */
/* Note space here */
Don't worry about leading whitespace (like the newline left in the buffer from the previous scanf call) because the numerical formats (and some others) automatically skips leading whitespace. You might want to read this reference.
Do not use gets to read string. Its a pain! Better to use fgets.
fgets(info[count].name, 20, stdin);
Be careful when using it with scanf. In such case these functions read the \n character left behind by the previous scanf call.
You can consume this \n by placing a getchar after last scanf.
scanf("%lf", &info[count].examScore);
getchar();
or even better
scanf("%lf", &info[count].examScore);
while(getchar() != '\n');
Anticipating your next question(s):
Replace
info[count].name != 'END'
by
strcmp(info[count].name, "END") != 0
You cannot compare strings with the != operator, you muist use the strcmp function.
Furthermore :
you declare int count = 1; but that means that the first element of the info array will not be used, you shoud have int count = 0;.
In the while condition info[count].name != 'END', info[count].name is not initialized, so your comparision will likely always be false, even if you correct the condition with strcmp as suggested before. In other words: you compare if the user has entered "END" before has has even entered "END".
Scanf is causing you this problem..
Use __fpurge(stdin) before scanf to resolve your problem.
Instead use fgets than scanf.
sorry i cannot comment yet.in addition to haccks's answer i will like to suggest you that you should use fflush(stdin) or fluahall() before scanf and you should use strcmp in place of info[count].name != 'END'.
Do not use gets to read a string. It's unsafe because it will overrun the buffer it writes into in case the input string is too large invoking undefined behaviour and most likely program crash due to segfault. The problem is that the last scanf call in the while loop leaves the trailing newline in the stdin buffer -
scanf("%lf", &info[count].examScore);
This newline is then read by gets in the next iteration of the loop causing gets to return. Read this C-FAQ Why gets call is skipped? You should use scanf instead of gets.
Replace the following statement
gets(info[count].name);
by
scanf(" %19s", info[count].name); // -1 for the terminating null byte
Note the leading space in the format string " %19s". This cause to scanf to read and discard any number of extraneous (including zero) whitespace characters lying in the stdin buffer. 19 means that scanf will write at most 19 characters into the buffer and then append the null byte at the end. This is called maximum field width and ensures that in case the input string is too long, scanf does not overrun the buffer which would invoke undefined behaviour.
you can't compare string using logical operator first error, second '' can contains only char means any single character it may be number, symbol etc. These are two problems and some gets and puts even scanf will skip the words so you have to use some times two input predefined functions I used some time because any of them will read.
scanf(some input);
gets(some input);
any one of them will work I used it personally.
This question already has answers here:
How do you allow spaces to be entered using scanf?
(11 answers)
Closed 6 years ago.
I'm trying to run the following code in the basic ubuntu gcc compiler for a basic C class.
#include<stdio.h>
struct emp
{
int emp_num, basic;
char name[20], department[20];
};
struct emp read()
{
struct emp dat;
printf("\n Enter Name : \n");
scanf("%s", dat.name);
printf("Enter Employee no.");
scanf("%d", &dat.emp_num);
//printf("Enter department:");
//fgets(dat->department,20,stdin);
printf("Enter basic :");
scanf("%d", &dat.basic);
return dat;
}
void print(struct emp dat)
{
printf("\n Name : %s", dat.name);
printf("\nEmployee no. : %d", dat.emp_num);
//printf("Department: %s", dat.department);
printf("\nBasic : %d\n", dat.basic);
}
int main()
{
struct emp list[10];
for (int i = 0; i < 3; i++)
{
printf("Enter Employee data\n %d :\n", i + 1);
list[i] = read();
}
printf("\n The data entered is as:\n");
for (int i = 0; i < 3; i++)
{
print(list[i]);
}
return 0;
}
I want the name to accept spaces.
The problem comes when I'm entering the values to the structures. I am able to enter the name the first time but the subsequent iterations don't even prompt me for an input.
I've tried using fgets, scanf("%[^\n]",dat.name) and even gets() (I was desperate) but am the facing the same problem every time.
The output for the 1st struct is fine but for the rest is either garbage, the person's last name or just blank.
Any ideas?
When reading a string using scanf("%s"), you're reading up to the first white space character. This way, your strings cannot include spaces. You can use fgetsinstead, which reads up to the first newline character.
Also, for flushing the input buffer, you may want to use e.g. scanf("%d\n") instead of just scanf("%d"). Otherwise, a subsequent fgets will take the newline character and not ask you for input.
I suggest that you experiment with a tiny program that reads first one integer number and then a string. You'll see what I mean and it will be much easier to debug. If you have trouble with that, I suggest that you post a new question.
The problem is that scanf("%[^\n",.. and fgets don't skip over any whitespace that may be left over from the previous line read. In particular, they won't skip the newline at the end of the last line, so if that newline is still in the input buffer (which it will be when the last line was read with scanf("%d",..), the scanf will fail without reading anything (leaving random garbage in the name array), while the fgets will just read the newline.
The easiest fix is to add an explicit space in the scanf to skip whitespace:
printf("\n Enter Name : \n");
scanf(" %19[^\n]", dat.name);
This will also skip over any whitespace at the beginning of the line (and blank lines), so may be a problem if you want to have a name that begins with a space.
Note I also added a length limit of 19 to avoid overflowing the name array -- if the user enters a longer name, the rest of it will be left on the input and be read as the employeee number. You might want to skip over the rest of the line:
scanf("%*[^\n]");
This will read any non-newline characters left on the input and throw them away. You can combine this with the prior scanf, giving you code that looks like:
printf("\n Enter Name : ");
scanf(" %19[^\n]%*[^\n]", dat.name);
printf("Enter Employee no. : ");
scanf("%d%*[^\n]", &dat.emp_num);
printf("Enter department : ");
scanf(" %19[^\n]%*[^\n]", dat.department);
printf("Enter basic : ");
scanf("%d%*[^\n]", &dat.basic);
This will ignore any spurious extra stuff that someone enters on a line, but will still have problems with someone entering letters where numbers are expected, or end-of-file conditions. To deal with those, you need to be checking the return value of scanf.
What you have tried was:-
scanf("%[^\n]",dat.name)
In this you forgot to specify the specifier.
You can try to use this:-
scanf ("%[^\n]%*c", dat.name);
or fgets() if you want to read with spaces.
Note:- "%s" will read the input until whitespace is reached.
I'm writing this to get student information (full name, id and gpa for the last 3 trimester, so I used structures and a for loop to plug in the information however, after 1st excution of the for loop (which means at student 2) my 1st and 2nd input are shown on screen together. How could I prevent this from happening in a simple and easy way to understand? ( P.S: I already tried to put getchar(); at the end of the for loop and it worked, however; I'm not supposed to use it 'cause we haven't learnt in class)
The part of the c program where my error happens:
#include <stdio.h>
struct Student {
char name[30];
int id;
float gpa[3];
};
float averageGPA ( struct Student [] );
int main()
{
int i;
float average;
struct Student studentlist[10];
i=0;
for (i; i<10; i++)
{
printf("\nEnter the Student %d full name: ", i+1);
fgets(studentlist[i].name, 30, stdin);
printf("Enter the Student %d ID: ", i+1);
scanf("\n %d", &studentlist[i].id);
printf("Enter the Student %d GPA for the 1st trimester: ", i+1);
scanf("%f", &studentlist[i].gpa[0]);
printf("Enter the Student %d GPA for the 2nd trimester: ", i+1);
scanf("%f", &studentlist[i].gpa[1]);
printf("Enter the Student %d GPA for the 3rd trimester: ", i+1);
scanf("%f", &studentlist[i].gpa[2]);
}
average = averageGPA(studentlist);
printf("\n\nThe average GPA is %.2f", average);
return 0;
}
float averageGPA (struct Student studentlist[])
{
int i;
float total = 0.0, average = 0.0;
for (i=0; i<10; i++)
{
total = studentlist[i].gpa[0] + studentlist[i].gpa[1] + studentlist[i].gpa[2];
}
average = total / 30 ;
return average;
}
Computer output:
Enter the Student 1 full name: mm
Enter the Student 1 ID: 12
Enter the Student 1 GPA for the 1st trimester: 3
Enter the Student 1 GPA for the 2nd trimester: 4
Enter the Student 1 GPA for the 3rd trimester: 3
Enter the Student 2 full name: Enter the Student 2 ID: <<<<< Here is the problem!!
Try eating the newline after the last scanf:
scanf("%f ", &studentlist[i].gpa[2]);
^
This is very much like your getchar solution. It's actually superior to getchar, since it only discards whitespace.
But you have to use getchar() to discard the newline character that is still in the input buffer after your last scanf("%f"), which according to given format converts a float and leave in the buffer all other chars.
If you can't use getchar(), use another fgets() at the end of the loop.. but of course getchar() would be better
Edit for explanation: whenever you type on your keyboard characters go in a input buffer waiting to be processed by your application. getchar() just "consumes" one character from this buffer (returning it), waiting for a valid char if the buffer is empty. scanf("%f") only "consumes" characters resulting in a float. So, when you type "5.12<enter>", scanf reads and removes from buffer "5.12" leaving "<enter>". So the next fgets() already finds a newline in the buffer and returns immediately; that's why you should use getchar(): ignoring its returning value you successfully discard "<enter>" from the buffer. Finally, please note that if in the buffer there is only "<enter>", scanf("%f") discards it (since it cannot be converted in a float) and waits for another input blocking application.
One last note: input stream is buffered by your OS default policy, in the sense that application does not receive any character until you type "<enter>".
Use scanf in following way to read the student name:
scanf(" %[^\n]",studentlist[i].name);
The first space in the format specifier is important. It negates the newline from previous input. The format, by the way, instructs to read until a newline (\n) is encountered.
[Edit: Adding explanation on request]
The format specifier for accepting a string is %s. But it allows you to enter non-whitespace characters only. The alternative way is to specify the characters that are acceptable (or not acceptable, based on the scenario) within square brackets.
Within square brackets, you can specify individual characters, or ranges, or combination of these. To specify characters to be excluded, precede with a caret (^) symbol.
So, %[a-z] would mean any character between a and z (both included) will be accepted
In your case, we need every character other than the newline to be accepted. So we come up with the specifier %[^\n]
You will get more info on these specifiers from the web. Here's one link for convenience: http://beej.us/guide/bgc/output/html/multipage/scanf.html
The space in the beginning actually 'consumes' any preceding white space left over from previous input. You can refer the answer here for a detailed explanation: scanf: "%[^\n]" skips the 2nd input but " %[^\n]" does not. why?
I would just say no to scanf(). Use fgets() for all input fields and convert to numeric with atoi() and atof().