segmentation fault after second fgets - c

I'm pretty new to C and I'm working on a simple practice problem with structures. My code asks for input termed "employee information", asking for their name (a string), the date they were hired (a string) and what the salary will be (an integer).
The first fgets works fine, and sticks a newline in the buffer as usual. The second then takes input and promptly jumps out the program.
I've tried sticking extra scanf()'s and getchar()'s in lots of different places to get rid of the newline, but nothing seems to help.
I even tried all of this with the debugger, and the only thing I get is a segmentation fault, which I don't really understand.
I have looked around, asked people and nothing seems to solve this problem. I am more than aware of all the questions similar to this one, but for some reason I just can't get it to work.
#include <stdio.h>
#include <stdlib.h>
/*****
Initialize a structure to read in and record the employee name, the hire
date and their salary
******/
//Define structure
struct employee
{
char *name;
char *hireDate;
float salary;
};
int main()
{
//First hardcode an employee
struct employee emp1;
emp1.name = "Karl";
emp1.hireDate = "May 10, 2019";
emp1.salary = 60000.00f;
//Now print off this employee
printf("The first employee's name is %s, he was hired on %s and will make %f per year\n", emp1.name, emp1.hireDate, emp1.salary);
printf("The next employee is you! Please enter the following information\n");
//Now ask user for second employee
struct employee emp2;
printf("Please enter your name: \n");
fgets(emp2.name, 30, stdin);
//This one works just fine, it produces name\n
printf("Please enter the date you were hired in regular format (i.e. May 10, 2019)\n");
//I had hoped this scanf() would absorb the above newline
scanf(" ");
//This takes input, and then jumps out of the program
fgets(emp2.hireDate, 30, stdin);
printf("Please enter your salary: \n");
scanf(" ");
scanf(" %f",&emp2.salary);
//Now print off this stuff that was typed in
printf("The first employee's name is %s, he was hired on %s and will make %f per year\n", emp2.name, emp2.hireDate, emp2.salary);
return 0;
}

You shouldn't declare those pointers like that unless you malloc() memory for them at some point. Since you're limiting your input to 30 chars statically you should declare your strings inside the struct as arrays: char name[31] and char hireDate[31]. You need that extra char in your arrays to hold the '\0' which terminates the string.
Please remember that fgets() takes the buffers size as 2nd parameter, not the number of characters to read. To allow the user to input a maximum of 30 characters you'd pass 31 as 2nd argument to fgets().

You don't have allocated memory to store the values read by gets.
In emp2, the pointers are not initialized, the first call to fgets could also segfault.
You need to allocate memory to hold the values, either by using malloc, or by defining your string fields as char name[30] for example.

Related

C: Loop only prints the last index

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...

C Program not printing the string in a structure

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.

C printf function data formats

Very simple C printing question!
#include <stdio.h>
#include <conio.h>
int main() {
int Age = 0;
printf("Enter your Age\n");
scanf("%d",&Age);
char Name;
printf("Enter your Full name\n");
scanf("%s",&Name);
printf("My name is %s and I am aged %d" ,&Name,Age);
return 0;
}
When I input "blah" and 1, for some reason this returns:
"My name is Blah and I am aged 1929323232"
I presume I am misunderstanding a data format in either the scanf or the printf functions but can't work it out.
The problem is because of line
char Name;
Name is of type char. That means that it is supposed to store only one character. As a result
1. The scanf() is not able to store the input text properly (this will result in a crash in most cases or other undefined behaviour depending on the system - which judging by the output you provided is what you got)
2. (if the code didn't crash) Treating Name as a string with the %s argument in printf() essentially outputs garbage.
The type that corresponds to strings in C is char * (or char[]). Essentially, changing Name to some statically allocated char-array while performing the necessary changes in the next lines should fix your error:
char Name[256]; //allocated 256 bytes in Name array
printf("Enter your Full name\n");
scanf("%s",Name); // removed & before Name
printf("My name is %s and I am aged %d" ,Name,Age); // same here
You could also opt to go with a dynamically allocated string of type char * but I guess that's a different topic altogether.
As a general suggestion, I think you should look at pointers more closely. Especially in C, almost all string operations involve being aware of pointer mechanisms.

Interactive, randomized program in C

My goal with this program is to incorporate the users inputs into a sort of interactive/randomized story but I'm not sure how I'm supposed to get the inputs from the users to fit between *ptrDescription, *ptrBeginning, *ptrMiddle, and *ptrEnd. Any help would be much, much appreciated!
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
#include <string.h>
#include <ctype.h>
int main(void){
int i;
char name[20];
char color[20];
int age;
char sentence[1];
//array of pointers to char arrays
char *ptrDescription[]={"the painfully handsome","the one and only","who seemed much older than"};
char *ptrBeginning[]={"was blissfully ignoring","could clearly see","had no idea"};
char *ptrMiddle[]={"the huge truck","the falling meteor","the bucket of milk","the mailman","the most powerful wizard"};
char *ptrEnd[]={"that was barreling toward them.","on the horizon."};
srand(time(NULL));
printf("Enter your first name: ");
scanf("%s", &name);
printf("\nEnter your age: ");
scanf("%d", &age);
printf("\nEnter your favorite color: ");
scanf("%s", &color);
for (i = 0; i < 1; i++)
{
//strcpy(sentence,ptrDescription[rand()%3]);
//strcat(sentence," ");
//strcat(sentence,ptrBeginning[rand()%3]);
//strcat(sentence," ");
//strcat(sentence,ptrMiddle[rand()%5]);
//strcat(sentence," ");
//strcat(sentence,ptrEnd[rand()%2]);
//strcat(sentence,".");
//sentence[0]=toupper(sentence[0]);
puts(sentence);
}
getch();
return 0;
}
EDIT:
I've edited a section of my code so that directly following for (i = 0; i < 1; i++) it now looks like this:
snprintf(sentence, sizeof sentence,"%s, %s %d year old, %s %s %s %s", name, ptrDescription[rand()%3], age,ptrBeginning[rand()%3], ptrMiddle[rand()%5], ptrEnd[rand()%2]);
There are tons of strange characters after the sentence in the output, like Japanese characters and stuff. I'm not sure why they're there, though. This is what it looks like exactly:
"Enter your first name: Justin
Enter your age: 20
Justin, the arrogant 20 year old, was purposefully ignoring the most powerful wizard that was barreling toward them. 汽$0HβHζ(テフフフフフフフフフフフフフH・(DキHH広$0陏&・汽$0タHζ(テフフフフフフフフフフフフフフフH WH・ H櫛H・t9HνHテ<"
Anyone know how I can get rid of them?
If you already have a name and an age, it's just a matter of inserting them into the correct place in sentence, right? So strcat(sentence, name) would work for name. age is a little trickier since you have to format the number first, and strcat won't do it for you. One solution would be to use sprintf(buf, "%d", age), and then concatenate buf (which is a scratch char array you would have to declare).
Any time you work with strings in C, you have to be concerned about having enough space in the target buffer. Your program can run out of space during both input and output. For the output, I would get rid of sentence altogether; since you just end up writing to stdout I would printf("%s", [part]) each part as you go along. For reading, scanf supports adding a length argument to the format string.
If you use one of the *printf functions, there are 2 things you must be careful about:
The arguments you pass are correct for the format string you use
Your buffer ends up null-terminated
Your current problem is with #1 - your format string promises 7 arguments to follow, but you only supply 6. snprintf grabs a "random" 7th value from the stack, interprets it as a char pointer, and copies whatever it finds there to sentence. You could see similar problems if your format string promised a char pointer but you placed an int in a given position. In this case the format string is a constant, so a smart compiler can validate that your format string matches the subsequent parameters. You'll want to get into the habit of taking compiler warnings seriously and not ignoring them.
The second point could be an issue if your sentence ended up bigger than your sentence buffer. If there is no room for a null-terminator, one won't be applied. You can check the return value of snprintf, or you can defensively always write a 0 to the last array position.

An assignment requires that I gather a name with 2 scanf's

So here is my first ultra beginner computer programming question in C.
I need to set it up so that someone can type in their full name on the input. Here is part of the specs -
"You'll have to do a little thinking to figure out how to get the printed names lined up with all the other columns. The first hint is that it involves joining strings together, something called concatenation. Give it a go, and if you can't figure it out, look at the next document in this folder; it contains additional hints. Part of the purpose of this assignment is to implicitly teach you concatenation. Do NOT use tabs (\t) and make sure your C/C++ editor does not produce tab characters.
Do NOT use gets() in this program. Use scanf() for inputting the interactive information. If you try to use gets(), you may get VERY frustrated.
In essence, all numbers appearing in the report should be right-justified and decimal-aligned. All numbers appearing in the summary should appear without leading spaces (other than the one which normally separates the number from the previous word). Hourly wage amounts CAN be less than 10.00, so be very careful with your formatting. The sample output can appear correct, but you can still be docked a half-point if things don't align properly with hourly wages under $10.00."
Additional hints:
You may assume that the employee name is always two names, a first name and a last name separated by a space. Also assume that there are never any spaces within a first name or within a last name. This allows you to use two scanf() calls instead of one gets() call. gets() would introduce some oddities that make things not work correctly later down the line.
You may also assume that neither name exceeds 10 characters in length.
The input from the Process another employee? question should be a single character. Assume that N or n will stop the loop, but that any other character will continue the loop.
Anyone know how to do this? When I use gets(which he says not to do), the loop screws up on the 2nd time around and it asks for the name and salary all in one line. And if I try to use 2 scanf statements, I get a crash or only 1 of the names input.
I was thinking the only way to do it is by outputting the names to a text file, then reading them in again. But is there some other way? I'm not allowed to ask for the names individually. A user might type a full name with one space, as it says in the specs.
Here is the code I wrote so far. I also need totals for all the gross', overtime hours and regular hours.
//stupid program
#include <stdio.h>
#include <strings.h>
#include <math.h>
//global variables
FILE *reportfile; //output file
char department[21];
int count;
char name[21];
float hoursworked;
float hourlywage;
float overtimehoursworked;
float overtimehourlywage;
float gross;
char again;
char firstname;
char lastname;
float tothoursworked;
float totovertimehoursworked;
float totgross;
const float overtimerate = 1.5;
const float overtimethreshold = 40; //hours needed to get overtime
//function prototypes
void GetInfo(void);
void Finalreport(void);
//main
int main(void)
{
reportfile = fopen("c:\\class\\kpaul-pay.txt","w"); //open output file
//////////////////////////////////////////////////
// initialize accumulating variables
/////////////////////////////////////////////////
count = 0;
tothoursworked = 0;
totovertimehoursworked = 0;
totgross = 0;
GetInfo();
fclose(reportfile); //close output file
return 0;
}
void GetInfo (void)
{
printf("Mountain Pacific Corporation\n");
printf("Department Salary Program\n\n");
printf("Please enter the name of the department: ");
gets(department);
fprintf(reportfile, "Mountain Pacific Corporation\n");
fprintf(reportfile, "Department Salary Program\n\n");
fprintf(reportfile, "%s\n\n", department);
fprintf(reportfile, "Employee Reg Hrs Overtime Hrs Gross\n");
fprintf(reportfile, "-----------------------------------------------------------------\n");
do {
printf("\nEnter employee #1: ");
gets(name);
printf("Enter the hourly wage of %s", name);
scanf("%f", &hourlywage);
printf("\nEnter total number of hours: ");
scanf("%f", &hoursworked);
if (hoursworked<=overtimethreshold)
overtimehoursworked = 0;
else if (hoursworked > overtimethreshold)
overtimehoursworked = hoursworked - overtimethreshold;
gross = (hoursworked*hourlywage) + (overtimehoursworked*overtimehourlywage);
fprintf(reportfile, "%s%16.2f(%4.2f)%12.2f(%4.2f) $%7.2f", name, hoursworked, hourlywage, overtimehoursworked, hourlywage * overtimerate, gross);
tothoursworked = tothoursworked + hoursworked;
totovertimehoursworked = totovertimehoursworked +overtimehoursworked;
totgross = totgross + gross;
printf("\n");
printf("Would you like another conversion? ");
scanf ("%s", &again);
printf("\n\n");
} while (again!='N' && again!='n');
}
Careful use of the scanf format string is needed. %s indicates a string that is delimited by a white space, in non technical terms, scanf will read characters until it finds a whitespace character such as a tab or space.
This poses a problem for reading multiple words, this can be solved by using multiple %s in the format string, you must provide a seperate array for each word as an argument after the format string e.g.
scanf("%s%s%s", string1, string2, string3)
This scanf call expects three words from the user, where each word is delimited by a white space. Make sure there is allocated memory for each of arrays though otherwise you will write into memory the program does not own normally causing the program to crash. The exact same effect to the code above can be achieved by using scanf multiple times with a single %s in the format string.
If you need everything as one string the same array you can use strcat which will concatenate two strings together. The function is simple to use, but once again make sure your arguments are allocated arrays before calling strcat otherwise it will likely cause a crash or unexpected behaviour.
Here are references to both functions:
scanf - http://www.cplusplus.com/reference/clibrary/cstdio/scanf/
strcat - http://www.cplusplus.com/reference/clibrary/cstring/strcat/
Hope this helps =]
scanf with the format %s will accept a whitespace-delimited string.
The crash is most likely caused by overflowing (or failing to reserve space for) the input string.

Resources