so, I am basically building an array of students with their evaluation.
I made a basic struct :
struct Eleve{
char Nom[100];
int Note;
};
struct Eleve *array;
Then asking for how many children I want to input into my array, then loop and asking information for each child. But, when I enter an input into scanf("%s\n", array[i].Nom); the code stop.
int nbEleves;
float total = 0;
printf("Nombre?!\n");
scanf("%d", &nbEleves);
array = malloc(nbEleves * sizeof(struct Eleve));
int i = 0;
while (i < nbEleves) {
printf("Nom? ");
scanf("%s\n", array[i].Nom);
printf("La note? ");
scanf("%d\n", &array[i].Note);
total = total + array[i].Note;
i++;
}
It doesn't even go on the next printf. I don't understand why, because I don't get any build error or execution errors on this line. Eventually I would have looked at the format if by any chance I'd get an error there, but nothing. No errors, just not getting to the next step of the program.
I think my scanf looks right, and I've got no warnings on it. So, what can prevent the code to go further?
The data I entered in Nom is test.
You should not use scanf! This is an age old discussion that use of scanf requires care and is dangerous. Disadvantages of scanf.
Instead use fgets().
Check this answer How to read from stdin with fgets()? or this
Not able to input a string with spaces in a loop in C
But if you still insist on using scanf(), this may be useful:
while(i<nbEleves){
printf("Nom? ");
scanf(" %[^\n]",array[i].Nom);// remove the \n and notice the space before %[^\n]
printf("La note? ");
scanf("%d", &array[i].Note);
total = total + array[i].Note;
i++;
}
Also I suggest that you must free the memory you allocated using free() in order to prevent memory leaks.
free(array);
Remove the \n from scanf("%s\n", array[i].Nom); and scanf("%d\n", &array[i].Note); or press CTRL + D after you inserted the value.
Related
I am new to C, so excuse me for asking a seemingly easy question. but I have a loop which loops depending on the user, and inside the loops it asks for a name and an age, however when I go to print, it only prints the last entry and not all the entries I want.
#include <stdio.h>
int main()
{
int size,age;
char name [30];
printf("How long to loop for: ");
scanf("%d", &size);
for (int i=0; i<size; i++)
{
printf("Enter first name: ");
scanf("%s", name);
printf("Enter %s's age: ",name);
scanf("%d",&age);
printf("name: %s, age: %d\n", name,age);
}
return 0;
}
Right now your name and age variables will only hold the last input they received, thats why you're program only prints the last index. You need an array of int as well as an array of char [] to "hold" each input they receive, otherwise those inputs get lost and you only get input for the last index.
your variables should be initialized like:
int size;
printf("Enter size: ");
scanf("%d", &size);
int age [size];
char name[size][30];
in order to get user input and print each entry you will need to access each index by looping through the array size.
to access each index:
age[i]
name[i]
Please Note: using scanf to get a string from user input is generally bad, as it can cause problems if you happen to use a space(such as inputting your full name for example) in your input, and it can cause a buffer overflow if your string is longer than the buffer. Instead you can use fgets, but for your question it isn't necessary, just something to considered in the future in case you want to have spaces in your inputs.
Are you sure that the first scanf actually reads a number.
You want the following to check
if (scanf(" %d", &size) != 1) { // Note space - Eats white space/new lines
printf("Error - invalid size\nExiting\n");
return -1;
}
Prevent buffer overruns
Use:
scanf(" %29s", name); // See above
See point 1 for age
I think this will solve your problems
Okay so if I am understanding you correctly, you are looping through the for loop size number of times and each time you are setting the contents of the variables age and name. It sounds like you are expecting each iteration through the loop to save your entered instance of age and name but this is not the case.
Since you only have one age and one name variable they are going to store whatever the most recently taken input was. So this means whatever was entered on the last loop is what was last written into the variables name and age.
If you then try to print the contents of those variables you are going to see whatever was most recently entered into those variables. you would need more than one name and more than one age variable to write into if you were trying to save multiple entries.
Okay so Anthony, maybe you need to try a little bit of review when it comes to allocating memory. So when we declare the variable
int age;
we are telling the compiler to allocate enough memory to store one single integer that we will call 'age' in our program. since we only asked for enough memory to store one integer, every time you scanf() into the age variable, the single integer is written into that space.
What you want is to store a new integer every time you do another run through your for loop. So, you could declare an array of integers as follows.
int age[20];
Now we have asked the compiler to allocate enough space to store 20 different integers. there will an integer in position age[0] and another integer in age[1] and another in age[2] so on. So now you could use a loop to index through your array and store each entered age from stdin into a different position in the array as follows
for(int i=0; i<20; i++)
{
printf("Please enter your age: ");
scanf("%i", &age[i]);
}
So this loop starts with i=0 and so enters the input from stdin to age[0], the next iteration through the loop has i=1 and so enters the stdin input to age[1] and so on.
Hopefully this clarifies things a bit more?
A major fault is that your printfs will not be displayed as they are buffered.
You need a fflush after them
i.e.
printf("How long to loop for: ");
fflush(stdout);
And
printf("Enter first name: ");
fflush(stdout);
etc...
The below code runs perfectly with scanf(), but i want to input characters along with white space. I have tried gets() and fgets() (commented below), but its not working (its skipping to next iteration in the loop & displaying blank(NULL) during output for Name input used by fgets()). Any idea how to do it?
PS: I've tried fputs() with sample programs, its working fine. But i am little confused while using structure pointer.
#include <stdio.h>
#include <stdlib.h>
struct details {
uint Id;
char Name[20];
};
int main() {
int tot, i;
struct details *info;
printf("Enter no. of details to be stored = ");
scanf("%d", &tot);
info = (struct details *)malloc(tot*sizeof(struct details));
for(i=0; i<tot; i++) {
printf("%d)Id = ", i+1);
scanf("%d", &info->Id);
printf("%d)Name = ", i+1);
fgets(info->Name, 20, stdin); //How to make fgets() work??
//scanf("%s", &info->Name); //works fine with scanf()
info++;
}
for(i=0; i<tot; i++) {
--info;
}
printf("\nDisplaying details:\n");
for(i=0; i<tot; i++) {
printf("%d)Id = %d\n%d)Name = %s\n", i+1, info->Id, i+1, info->Name);
info++;
}
return 0;
}
output:
[xyz#xyz:Temp] $ ./fgets_Struct
Enter no. of details to be stored = 2
1)Id = 111
1)Name = 2)Id = 222
2)Name =
Displaying details:
1)Id = 111
1)Name =
2)Id = 222
2)Name =
[xyz#xyz:Temp] $
The problem comes from the "%d" scanf. It does not consume the line terminator, so the next read will interpret it as an empty line.
Your info allocation is wrong too. What you should use to allocate the size is not the size of info, but the size of an element pointed to by info.
printf("Enter no. of details to be stored = ");
scanf("%d\n", &tot); // <-- must consume end of line here
info = (struct details *)malloc(tot*
sizeof(*info)); // <-- use size of the pointed object, not the pointer
Also, tinkering with your info pointer is unnecessary and dangerous.
Something like that would be simpler and safer
for(i=0; i<tot; i++) {
printf("%d)Id = ", i+1);
scanf("%d\n", &info[i].Id)); // <-- store input in current info record
// and eat up the line terminator
printf("%d)Name = ", i+1);
fgets(info[i].Name, // <-- idem
sizeof(info[i].Name), // fgets will guarantee no buffer overflow
stdin);
}
printf("\nDisplaying details:\n");
for(i=0; i<tot; i++) {
printf("%d)Id = %d\n%d)Name = %s\n", i+1, info[i].Id, i+1, info[i].Name);
As for scanf / fgets, in your case (with "%s" format given to scanf), they should both read the input until a new line is entered, spaces included.
EDIT: I said something wrong here, sorry and thanks to chux for correcting me.
scanf ("%s") will indeed stop at the first white space. If you want the whole line, the easiest way is to use fgets. If you really want to use scanf, you'll need a syntax like "%[^\n]%*c".
To make sure your buffer does not overflow if the user types more than 20 characters, you can either
use "%19[^\n]%*c" as scanf format (20th character is needed for the '\0' terminator), or
use fgets with 20 passed as buffer size (fgets takes care of the terminator, it will read at most 19 characters to be able to add the '\0' without overflowing).
Of course you should use sizeof to compute max buffer size, like for instance:
fgets(info[i].Name, sizeof(info[i].Name), stdin);
Thus you won't have to modify the value if you decide, for instance, to have your buffer size changed to 50 characters.
First, you have a serious bug in your code.
info = (struct details *)malloc(tot*sizeof(info));
sizeof(info) returns either 4 or 8 (depending on the platform, 32 or 64 bit), but has nothing to do with your struct size. So you are allocating memory for pointers, but are using that space for your struct. Writing your data into this will overwrite other data in memory (although probably not in that simple example), because you allocate way too few bytes. What you want to use is sizeof(struct details) which would return 24.
Now to your input problem. Basically, scanf and gets should not be used together like that. The problem is that scanf reads what you type until you press return, which is not included. When you call gets after that, it sees this return and returns immediately with an empty string. If you change all your input calls to gets, it works perfectly. Example:
char test[256];
printf("Enter no. of details to be stored = ");
gets(test);
tot = atoi(test);
between the line scanf() for ID and fgets() put the line getchar()
like this
scanf("%d",&(info->Id));
getchar();
I had the same problem! Actually, I came to this page to get the solution and then I remembered the buffer!! So this is the way I solved it:
[...]
cout << "\nType the name: " ;
fflush(stdin); (you always have to clean the buffer before saving chars to a variable)
gets(pers.nom);
[...]
Cheers!!
P.S. cin and cout are from C++ but work like scanf and printf
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.
int main()
{
//Define Variables
char studentName;
//Print instructions to fill the data in the screen
printf("Please type in the Students name:\n");
scanf("%s", &studentName);
printf("\n\n%s", &studentName);
return 0;
}
Seeing the above code, I am only printing to screen out the first word when I type in a sentence.
I know it is a basic thing, but I am just starting with plain C.
Read scanf(3) documentation. For %s is says
s Matches a sequence of non-white-space characters; the next
pointer must be a pointer to character array that is long
enough to hold the input sequence and the terminating null
byte ('\0'), which is added automatically. The input string
stops at white space or at the maximum field width, whichever
occurs first.
So your code is wrong, because it should have an array for studentName i.e.
char studentName[32];
scanf("%s", studentName);
which is still dangerous because of possible buffer overflow (e.g. if you type a name of 32 or more letters). Using %32s instead of %s might be safer.
Take also the habit of compiling with all warnings enabled and with debugging information (i.e. if using GCC with gcc -Wall -g). Some compilers might have warned you. Learn to use your debugger (such as gdb).
Also, take the habit of ending -not starting- your printf format string with \n (or else call fflush, see fflush(3)).
Learn about undefined behavior. Your program had some! And it misses a #include <stdio.h> directive (as the first non-comment significant line).
BTW, reading existing free software code in C will also teach you many things.
There are three problems with your code:
You are writing a string into a block of memory allocated for a single character; this is undefined behavior
You are printing a string from a block of memory allocated for a single character - also an undefined behavior
You are using scanf to read a string with spaces; %s stops at the first space or end-of-line character.
One way to fix this would be using fgets, like this:
char studentName[100];
//Print instructions to fill the data in the screen
printf("Please type in the Students name:\n");
fgets(studentName, 100, stdin);
printf("\n\n%s", &studentName);
return 0;
Try scanf("%[^\n]", &studentName); instead of scanf("%s", &studentName);
This is happening because %s stops reading the input as soon as a white space is encountered.
To avoid this what you can do is declare an array of the length required for your string.
Then use this command to input the string:-
scanf("%[^\n]s",arr);
This way scanf will continue to read characters unless a '\n' is encountered, in other words you press the enter key on your keyboard. This gives a new line signal and the input stops.
int main()
{
//Define Variables
char studentName[50];
//Print instructions to fill the data in the screen
printf("Please type in the Students name:\n");
scanf("%[^\n]s", &studentName);
printf("\n\n%s", &studentName);
return 0;
}
Alternatively you can also use the gets() and puts() method. This will really ease your work if you are writing a code for a very basic problem.
[EDIT] : As dasblinkenlight has pointed out...I will also not recommend you to use the gets function since it has been deprecated.
int main()
{
//Define Variables
char studentName[50];
//Print instructions to fill the data in the screen
printf("Please type in the Students name:\n");
gets(studentName); printf("\n\n");
puts(studentName);
return 0;
}
make the changes below and try it. I added [80] after the studentName definition, to tell the compiler that studentName is an array of 80 characters (otherwise the compiler would treat it as only one char). Also, the & symbol before studentName is not necessary, because the name of the array implicitly implies a pointer.
int main()
{
//Define Variables
char studentName[80];
//Print instructions to fill the data in the screen
printf("Please type in the Students name:\n");
scanf("%s", studentName);
printf("\n\n%s", studentName);
return 0;
}
Your problem is here
char studentName;
It is a char, not a string.
Try:
Define it as an array of chars like char studenName[SIZE];.
allocating memory dynamically using malloc:
.
char buffer[MAX_SIZE];
scanf("%s", &buffer);
char * studentName = malloc (sizeof(buffer) + 1);
strcpy (studentName , buffer);
Beginner with C here. I am trying to run a loop where strings and ints are entered into various fields of a struct. When prompted for a 'last name', the user can press enter with no other input and the loop should end.
The problem is that with this code, the loop doesnt end (last name and first name entry requests run together on the same line) and the value for salary always comes out wrong (0 or some large number)
while (employee_num <= 2)
{
printf("Enter last name ");
fgets(employee[employee_num].last_name, sizeof(employee[employee_num].last_name), stdin);
if(strlen(employee[employee_num].last_name) == 0)
break;
printf("Enter first name ");
fgets(employee[employee_num].first_name, sizeof(employee[employee_num].first_name), stdin);
printf("Enter title ");
fgets(employee[employee_num].title, sizeof(employee[employee_num].title), stdin);
printf("Enter salary ");
fgets(strng_buffer, 1, stdin);
sscanf(strng_buffer, "%d", &employee[employee_num].salary);
++employee_num;
getchar();
}
If I try this code instead, I am able to exit the loop properly after the first run through it, but cannot exit after that (by pressing enter at the last name portion - perhaps a \n I cant seem to clear?):
char strng_buffer[16];
while (employee_num <= 5)
{
printf("Enter last name ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].last_name);
if(strlen(employee[employee_num].last_name) == 0)
break;
printf("Enter first name ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].first_name);
printf("Enter title ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].title);
printf("Enter salary ");
scanf("%d", &employee[employee_num].salary);
++employee_num;
getchar();
}
I am curious as to how to make this work as intended and what best practice would be for entries like this (ie use of sscanf, fgets, etc)
Thanks in advance!
The Loop breaks prematurely when it encounters the break statement
if(strlen(strng_buffer) == 0)
break;
The uninitialized character buffer strng_buffer, coincidently has null as the first character causing strlen to return 0
I believe you may have intended
if(strlen(employee[employee_num].last_name) == 0)
break;
as the loop terminatorm, and it was a typo in your part causing premature loop exit.
Assuming the fix mentioned by Abhijit, why transform the first into the second? Are you aware that the second behaves differently to the first, because of the addition of sscanf? If your intention was to shorten the first, the second seems quite bulky. Rather than adding sscanf to the situation, why not shorten the first by declaring a struct employee *e = employee + employee_num; and using that repetitively, instead of employee[employee_num]?
One "best practise" regarding fgets is to check it's return value. What do you suppose fgets might return, if it encounters EOF? What do you suppose fgets would return if it's successful?
One "best practise" regarding scanf is to check it's return value. In regards to the return value of scanf, I suggest reading this scanf manual carefully and answering the following questions:
int x = scanf("%d", &employee[employee_num].salary); What do you suppose x will be if I enter "fubar\n" as input?
Where do you suppose the 'f' from "fubar\n" will go?
If it's ungetc'd back onto stdin, what would your next employee's last name be?
int x = scanf("%d", &employee[employee_num].salary); What do you suppose x will be if I run this code on Windows and press CTRL+Z to send EOF to stdin?
int x = scanf("%d %d", &y, &z); What would you expect x to be, presuming scanf successfully puts values into the two variables y and z?
P.S. EOF can be sent through stdin in Windows by CTRL+Z, and in Linux and friends by CTRL+D, in addition to using pipes and redirection to redirect input from other programs and files.
The problem is that fgets returns the string with the line break (\n) included. So, even the user presses return without entering info, the string won't be empty. Also, your buffer size for salary is too small.
So, either you strip out the \n on every fgets or you change your check to:
if(strlen(employee[employee_num].last_name) == 1) break;
Also, when you're getting the buffer, change 1 to something bigger, like
fgets(strng_buffer, 10, stdin);
However, if you do want to strip out the \n from each fgets, you can do something like:
employee[employee_num].last_name[strlen(employee[employee_num].last_name)-1] = 0;
You can do this for every string or, better yet, create a function that does it.
EDIT: if you can guarantee that the user will press enter after each input then you can safely assume this. However if it's not always the case it's possible that the last character is not \n and just stripping this way might cause problems.