I created a structure that goes as follows:
typedef struct
{
char name[20];
char country[20];
int age;
float height;
} details;
Accompanying the structure definition are two functions that accept and display the details of the structure's members respectively:
void enter_details(details d1)
{
printf("Enter your name: ");
gets(d1.name);
printf("Enter your country: ");
gets(d1.country);
printf("Enter your age: ");
scanf("%d",&d1.age);
printf("Enter your height (in meters): ");
scanf("%d",&d1.height);
}
void show_details(details d1)
{
printf("Name: ");
puts(d1.name);
printf("\nCountry: ");
puts(d1.country);
printf("\nAge: %d",d1.age);
printf("\nHeight: %.2f m",d1.height);
}
The declaration of a structure object in the main() segment is relatively straightforward and goes as follows:
details d1;
While the compiler doesn't spit any errors as such, and neither does the invocation of the function that accepts information for the structure's members, invocation of the show_details() function spits out nothing but garbled, gibberish characters. What is causing this anomaly?
In C, arguments of a function are passed by value, not by reference, meaning that the argument you pass will be copied. So what you are dealing with inside your function is a copy of your object, not the actual object. In order to refer to the actual object, you need to pass a reference.
Also, avoid using scanf() (and gets()) to read input. Use fgets() instead.
So, you should change your code to:
void enter_details(details *d1)
{
printf("Enter your name: ");
getstring(d1->name, sizeof(d1->name)));
printf("Enter your country: ");
getstring(d1->country, sizeof(d1->country));
printf("Enter your age: ");
getint(&d1->age);
printf("Enter your height (in meters): ");
getint(&d1->height);
}
An alternative to scanf() to read a string:
char *getstring(char *str, int size)
{
if (!fgets(str, size, stdin)) {
fprintf(stderr, "Input error\n");
return NULL;
}
str[strcspn(str, "\n")] = '\0'; // fgets() reads the ending '\n' so null-terminate the string
return str;
}
And to read an int:
int getint(int *i)
{
char buffer[255];
if (!fgets(buffer, 255, stdin)) {
fprintf(stderr, "Input error\n");
return 0;
}
str[strcspn(str, "\n")] = '\0';
if (sscanf(buffer, "%d", i) != 1)
return 0;
return 1;
}
Do not use gets. Use fgets instead. fgets(d1.name, sizeof(d1.name), stdin);
You need to pass the reference to the struct otherwise you will work on the local copy of the structure. You can also return the structure.
void enter_details1(details *d1)
{
printf("Enter your name: ");
fgets(d1 -> name, sizeof(d1 ->name), stdin);
printf("Enter your country: ");
fgets(d1 -> country, sizeof(d1 -> country), stdin);
printf("Enter your age: ");
scanf("%d",&d1 ->age);
printf("Enter your height (in meters): ");
scanf("%d",&d1 ->height);
}
details enter_details(void)
{
details d1;
printf("Enter your name: ");
fgets(d1.name, sizeof(d1.name), stdin);
printf("Enter your country: ");
fgets(d1.country, sizeof(d1.country), stdin);
printf("Enter your age: ");
scanf("%d",&d1.age);
printf("Enter your height (in meters): ");
scanf("%d",&d1.height);
retunrn d1;
}
usage:
int main(void)
{
details d;
enter_details1(&d);
//or
d = enter_details();
/* .... */
}
BTW I hope that you know that height can be only entered in full meters. No fractions
Related
I'm trying to create a C program which collect's an applicant's information. When a user is prompted to enter their written subjects, the program writes rubbish data into the .csv file when they wrote one. And sometimes does the same when the number of subjects written is two.
I've tried to clear the buffer stream, but it's no use. Strangely, using different compliers like DevC++, Embarcadero DevC and VS Code produces different results.
Edit: I've also noticed the chances of the rubbish values being written into the file are lowered when the grades of the subjects is lower than the number of subjects written.
Attached below is the code. And an image of the output.
// C libraries.
#include <stdio.h> // Contains function prototypes for the standard input/output library functions, and information used by them.
#include <conio.h> // Contains function prototypes for the console input/output library functions.
#include <stdlib.h> // Contains function prototypes for conversions of numbers to text and text to numbers, memory allocation, random numbers and other utility functions.
#include <string.h> // Contains function prototypes for string-processing functions.
#include <time.h> // Contains function prototypes and types for manipulating the time and date.
#include <stdbool.h> // Contains macros defining bool, true and false, used for boolean variables.
struct Applicant
{
int applicationID;
int dateOfApplication;
char lastName[21];
char firstName[21];
char middleName[21];
char dateOfBirth[21];
int age;
char gender;
char address[100];
char phoneNumber[21];
char emailAddress[51];
char mobileNumber[21];
int numSubjectsWritten;
char csecSubjects[20][100];
char grades[20];
char programmeSelection[10];
};
struct Applicant getApplicantData()
{
struct Applicant applicant;
int i = 0;
int numSubjects;
// Asking for applicant input for various fields.
printf("| Personal |");
printf("\nEnter Last Name: ");
scanf(" %20s", &applicant.lastName);
fflush(stdin);
printf("\nEnter First Name: ");
scanf(" %20s", &applicant.firstName);
fflush(stdin);
printf("\nEnter Middle Name (If you don't have a middle name, leave this field blank.): ");
gets(applicant.middleName);
fflush(stdin);
/*
printf("\nEnter Date of Birth: ");
scanf(" %s", &applicant.dateOfBirth);
fflush(stdin);
printf("\nEnter Gender. 'M' for male, 'F' for female, (M|F): ");
scanf(" %c", &applicant.gender);
fflush(stdin);
printf("\n\n| Contact Information |");
printf("\nEnter Address: ");
gets(applicant.address);
fflush(stdin);
printf("\nEnter Phone Number: ");
gets(applicant.phoneNumber);
fflush(stdin);
printf("\nEnter Email Address: ");
gets(applicant.emailAddress);
fflush(stdin);
printf("\nEnter Mobile Number: ");
gets(applicant.mobileNumber);
fflush(stdin);
*/
printf("\n\n| Education |");
printf("\nEnter Number of Subjects Written: ");
scanf("%d", &applicant.numSubjectsWritten);
fflush(stdin);
while (i < applicant.numSubjectsWritten)
{
printf("\nEnter the subject: ");
gets(applicant.csecSubjects[i]);
fflush(stdin);
printf("\nEnter the grade for that subject: ");
scanf(" %c", &applicant.grades[i]);
fflush(stdin);
i++;
}
return applicant;
}
int main(void)
{
FILE *file = fopen("Data.csv", "a+");
int i, j;
if (!file)
{
printf("\nError! Can not open data file.\nPlease contact the Program Addmission Manager as soon as possible with the error message.");
exit(1);
}
else
{
struct Applicant applicant = getApplicantData();
//fprintf(file, "%s:%s:%s:%s:%c:%s:%s:%s:%s", applicant.lastName, applicant.firstName, applicant.middleName, applicant.dateOfBirth, applicant.gender, applicant.address, applicant.phoneNumber, applicant.emailAddress, applicant.mobileNumber);
fprintf(file, "%s:%s:%s:", applicant.lastName, applicant.firstName, applicant.middleName);
for (i = 0; applicant.csecSubjects[i][0] != '\0'; i++)
{
fprintf(file, " %s", applicant.csecSubjects[i]);
fflush(stdout);
fflush(stdin);
fflush(file);
fprintf(file, " ( %c):", applicant.grades[i]);
fflush(stdout);
fflush(stdin);
fflush(file);
}
}
return 0;
}
First problems I see:
Remove the & from all instances where you scanf a string
Don't use gets, or mix scanf and fgets
Don't fflush(stdin)
Instead of scanf, consider using a custom-made input method with condition checking and anything you need. I will give an example.
#define BUFFER_SIZE 512
void input(char* buffer){
memset(buffer, 0, BUFFER_SIZE); // Initializing the buffer.
fgets(buffer, BUFFER_SIZE, stdin);
strtok(buffer,"\n");
}
How to take input using that?
void main(){
int username[BUFFER_SIZE];
input(username);
}
A way to write a structure to a file is shown below.
void Structure_Print(Applicant* applicant, FILE* stream, int no_of_applicant){
if(no_of_applicant==0){
fprintf(stdout, "No applicant yet.\n");
return;
}
fprintf(stream, "%s:%s:%s:", applicant.lastName, applicant.firstName, applicant.middleName);
for (i = 0; applicant.csecSubjects[i][0] != '\0'; i++)
{
fprintf(stream, " %s:", applicant.csecSubjects[i]);
fprintf(stream, " %c:", applicant.grades[i]);
}
return;
}
Also, I noticed how you tried to make it readable while saving it in subject(grade) format. I recommend you to not do that. Your .csv file is just for database. Nobody is going to read it. So just store the data by comma or any character separator. It will make it easier to extract data later.
I'm currently learning c, then I'm playing with functions and data types, specifically in this case char[]'s.
The following code I've written declares a function called verifyMessage() and receives two parameters, name and gender.
When I execute the function, I pass the two parameters that the user enters through the console, but when I print the name it doesn't print anything.
#include <stdio.h>
int main() {
int i = 0;
double controlNumber = 21200164;
double number = 0;
char name[50];
char gender[1];
int attempts = 5;
int aux = 0;
do {
printf("Introduzca el numero de control: ");
scanf("%lf", &number);
if (controlNumber == number) {
printf("\nWrite your name: ");
scanf("%s", name);
printf("\nWrite your gender (M/F): ");
scanf("%s", gender);
verifyMessage(name, gender);
break;
} else {
i++;
}
} while (i < attempts);
return 0;
}
void verifyMessage(char name[50], char gender[1]) {
if ('M' == gender[0]) {
printf("\n\Name: %s", name);//Here doesn´t print the name
printf("\nMen");
} else if ('F' == gender[0]) {
printf("\nWoman");
} else {
printf("\nInvalid gender");
}
}
Using char gender[1]; with %s is dangerous because gender has room for only one element, so it can accept only strings upto zero characters (the only room will be occupied by terminating null-character)
On the other hand, %s will read positive-length strings (it cannot read strings with zero characters), so it will cause out-of-range access on successful read.
Allocate enough elements and set the maximum length to read (upto the number of elements minis one for terminating null-character) to avoid buffer overrun.
char name[50];
char gender[2];
/* ... */
printf("\nWrite your name: ");
scanf("%49s", name);
printf("\nWrite your gender (M/F): ");
scanf("%1s", gender);
Checking results (return values) of scanf() to check if they successfully read desired things will improve your code more.
I am trying to use fgets with structure, since I have to insert in character array. But when I use fgets it's not working properly. I can not enter value for the char array. Please help. Below is a sample program::
#include <stdio.h>
#include<string.h>
struct Student
{
int roll;
char name[50];
int age;
char branch[50];
char gender[1]; //F for female and M for male
};
int main()
{
struct Student s1;
printf("enter roll number of the student: ");
scanf("%d", &s1.roll);
printf("Enter student name: ");
fgets(s1.name, 50, stdin); // NOT WORKING ...
printf("Enter age number: ");
scanf("%d", &s1.age);
printf("Enter branch number: ");
scanf("%d", &s1.branch);
printf("Enter Gender: ");
scanf("%d", &s1.gender);
return 0;
}
First of all you need different format specifiers for different datatypes. So you need to use %c for a character and %[^\n] for a string containing spaces.
You also need to remove leading whitespaces before scanning a string, because a newline \n is left in the input buffer which would otherwise be read by %c and %[], as Weather Vane pointed out in a comment.
#include <stdio.h>
#include <string.h>
struct student
{
int roll;
char name[50];
int age;
char branch[50];
char gender; // can be a single character
};
int main(void)
{
struct student s1;
printf("Enter roll number: ");
scanf("%d", &s1.roll);
printf("Enter name: ");
scanf(" %49[^\n]", s1.name); // use %[^\n] to scan a string containing spaces
printf("Enter age: ");
scanf("%d", &s1.age);
printf("Enter branch name: ");
scanf(" %49[^\n]", s1.branch);
printf("Enter gender: ");
scanf(" %c", &s1.gender); // %c is the format specifier for a char
return 0;
}
fgets is not being bypassed, it's actually working as it should, what happens is that it reads the newline character that remains in the input buffer from the previous scanf, if you access s1.name you will see that it has a string ("\n\0") in it.
For name I have to insert space character too, so I used fgets
You can use scanf with [^\n] specifier which can read spaces. Mixing scanf with fgets is trouble, it can be done, but you should avoid it.
You should either use scanf only, or fgets only, in the latter case, if you need to convert strings to ints use sscanf or better yet strtol.
Your code has other issues, detailed in the comments with corrections:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
int roll;
char name[50];
int age;
char branch[50];
char gender; //F for female and M for male
};
For solution with scanf only it should, more or less, look like this:
void clear_buffer(){ // helper function to clear buffer
int c;
while((c = getchar()) != '\n' && c != EOF){}
if(c == EOF){
fprintf(stderr, "Fatal error!");
exit(EXIT_FAILURE);
}
}
int main()
{
struct Student s1;
printf("enter roll number of the student: ");
while (scanf("%d", &s1.roll) != 1){
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
} // if bad input ask again
printf("Enter student name: "); // the space before % clears blanks
while (scanf(" %49[^\n]", s1.name) != 1){ // will read the line until
fprintf(stderr, "Bad input, try again: "); // enter is pressed, provided
clear_buffer(); // that it's not larger than 49
}
printf("Enter age number: ");
while(scanf("%d", &s1.age) != 1){
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
}
printf("Enter branch number: ");
while (scanf(" %49[^\n]", s1.branch) != 1){ // branch is a string, %d
clear_buffer(); // specifier is for ints.
fprintf(stderr, "Bad input, try again: "); // note that I'm using width
} // limit (49) to avoid buffer overflow
printf("Enter Gender: ");
while(scanf(" %c", &s1.gender) != 1){ // only 1 character needed, use %c
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
}
}
For a solution with fgets only which, I would argue is better, you can do something like this:
int main(){
struct Student s1;
char temp[50];
printf("enter roll number of the student: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%d", &s1.roll) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter student name: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%49[^\n]", s1.name) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter age number: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%d", &s1.age) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter branch number: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%49[^\n]", s1.branch) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter Gender: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, " %c", &s1.gender) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
}
*scanf to parse ints still has a potencial flaw in case of overflow, there is no way of guarding against that, unless you use a more robust method like the aforementioned strtol.
There are some problems in this code which I am unable to figure it out. No.1 is with printf() which is just allowing me to enter name but date of birth. No.2 is maybe the the problem with if statement. No. 3 is that the else statement just stops the process but didn't show me the message.
int list_view(char *name, char *dob, char *id, char *phone_num, char *address, char *account, char *fixing, char *amount){
printf("To show you details, please enter the person name and id card number: \n");
printf("Enter your Name: ");
printf("Enter you Date of Birth: ");
if(fgets(name, sizeof name, stdin) && fgets(dob, sizeof dob, stdin)){
FILE * fr;
int one_by_one;
fr = fopen("/home/bilal/Documents/file.txt", "r");
for (int i=0; i<8; i++){
printf("\nHere is your "); /* listing is transferred above just to show ones */
while((one_by_one = fgetc(fr)) != EOF && one_by_one != '\n'){
printf("%c",one_by_one); /* display on screen*/
}
} /* end of for loop */
fclose(fr);
} /* end of if statement */
else{
printf("No access");
}
return 0;
}
First you should prompt and get each piece of input in turn. ie
printf("Enter your Name: ");
fgets(name, sizeof name, stdin); /// with some checking
printf("Enter you Date of Birth: ");
fgets(dob, sizeof dob, stdin); // with some checking
second,
sizeof name
is useless, its the size of a pointer. If these input buffers are supplied by the caller then they have to pass in a buffer size arg too.
You seem hung up on your 'if'.
This is what you need I think. If they dont enter a value for either prompt then you want to say 'no access' and return
printf("Enter your Name: ");
if(!fgets(name, namesize, stdin))
{
printf("no access");
return;
}
printf("Enter you Date of Birth: ");
if(!fgets(dob, dobsize, stdin))
{
printf("no access");
return;
}
I was wondering if it is possible to use fprintf() with a structure, because I know you can't use a "%" for the structure.
struct blackhole_register
{
int userID;
float blackhole_Mass;
char blackhole_ID[5];
char name_First[11];
char name_Last[16];
};
int main ()
{
struct blackhole_register input;
struct blackhole_register output;
FILE *blackhole_file;
if ((blackhole_file = fopen("Holter.txt","w")) == NULL)
{
printf("File location not found, the program will now end\n");
}
else
printf("Schwarzschild Radius Application by Jonathan Holter\n\n");
printf("\nFirst Name: ");
fgets(input.name_First,11,stdin);
printf("\nLast Name: ");
fgets(input.name_Last,16,stdin);
printf("\nUser ID: ");
fgets(input.userID,4,stdin);
printf("\nBlack Hole Name/ID: ");
fgets(input.blackhole_ID,20,stdin);
printf("\nBlack Hole Mass (Solar Masses): ");
fgets(input.blackhole_Mass,3,stdin);
This is what I have so far, any help would be wonderful!!
There is no magic %serialize-my-struct flag in printf format strings. You'll need to printf each field in the struct separately. Consider writing a function int print_blackhole_register(FILE*, const blackhole_register*).