Dynamically inputting values in a structure and printing these values - c

I wrote this code for a structure stud, that will input the number of students whose record is to be stored, and then print the same. I'm not sure where exactly I'm going wrong.
#include<stdio.h>
struct stud{
int id;
char name[50];
float points;
}s[1000];
int main(){
int i,n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&s[i].id);
scanf("%c",s[i].name);
scanf("%f",&s[i].points);
}
for(i=0;i<n;i++){
printf("ID: %d\n",s[i].id);
printf("First Name: %c\n",s[i].name);
printf("Points: %f\n",s[i].points);
}
return 0;
}

Change
scanf("%c",s[i].name);
to at least
scanf("%49s",s[i].name);
and the same for printf() call also. %c is for a single char, for a string, you'd need %s. Also, don't forget to check the return value of scanf() for success.
That being said, better to use fgets() for user input. Something like
if( fgets (s[i].name, sizeof(s[i].name), stdin) != NULL ) { /*......*/ }

Related

Creating a function to ask question from the user using scanf in C

I'm very new to C but am trying to make a function to ask the user a question using scanf, any suggestions of how I should go about this, this is currently what I have.
#include <stdio.h>
char name[100];
int num, days;
int askQ(char *question, char *x, char *answer) {
printf("%s", question);
fflush(stdout);
scanf(x,&answer);
return 1;
}
int main() {
askQ("hello? | ", "%s\n", &name);
printf("%s", name);
return 0;
}
A couple of things make this a bit tricky. Your requirement for an arbitrary format string makes it difficult to know how arguments are to be passed to scanf(). E.g. an int argument will require that you pass the address of the receiving variable to scanf(), whereas a string does not require the address because the "string" is already a pointer.
Similarly you should check the return value from scanf() to ensure that the input was processed as expected - that's hard for an arbitrary format string.
Perhaps, if you really need arbitrary input parsing you can use vscanf(), but at your level I would not recommend that.
If you choose to keep it simple so that you simply have a prompt with a single string response then you might do this:
#include <stdio.h>
int askQ(char *question, char *format, char *answer) {
printf("%s", question);
int i = scanf(format, answer);
return i == 1;
}
int main() {
char name[100]; // accept up to 100 chars input
if (askQ("hello? | ", "%s", name))
printf("Response: %s\n", name);
else
printf("Failed to get valid response from user\n");
}

How to fix format error for char* and char**

i'm writing a kind of phonebook program and i'm using typedef struct to store the name and number of the people. The user will input name and number as a string format, but when i try to scan it, the compiler gives me an error saying "format specifies type char * but the argument has type char **. Any solution on this? i really don't understand what it means. Error is on first loop on both scans any help would be appreciated. Thanks!
#include <stdlib.h>
#include <string.h>
typedef struct
{
char *name;
char *number;
}
person;
int main(void)
{
int n;
scanf("%i", &n);
person people[n];
for(int i = 0; i < n; i++)
{
scanf("%s", &people[i].name); // these 2 lines are buggy
scanf("%s", &people[i].number);
}
char *findName;
scanf("%s", &findName);
for(int i = 0; i < n; i++)
{
if(strcmp(people[i].name, findName) == 0)
{
printf("%s\n", people[i].number);
}
else
{
return 1;
}
}
return 0;
}
The statement
scanf("%s", &people[i].name);
is wrong for two reasons:
First of all the %s format expects a char * argument. By using the address-of operator you get a value of type char **. Mismatching format specifier and argument type leads to undefined behavior.
If you remove the address-of operator you will pass an uninitialized pointer, it's not pointing anywhere valid. This means scanf will write the string to some seemingly random location in memory. Again this leads to undefined behavior.
A simple way to solve the second issue, you could change the structure to contain arrays of characters instead, as in:
typedef struct
{
char name[64];
char number[64];
}
person;
Then use a limit in the format so the scanf function will not attempt to read a string longer than the arrays:
scanf("%63s", people[i].name); // The array decays to a pointer to its first element
Note that the length in the format is 63, so the string null-terminator will fit in the array.
Never use the "%s" format with scanf(). You cannot use it safely.
The problem is, that the user controls how many characters will get written into the memory buffer, but the program needs to supply the memory buffer without knowing how many characters the user is actually going to input. The result is invariable buffer overrun vulnerabilities of your program. Don't do this.
Instead, use allocating input functions. For scanf(), you need to add the "m" modifier:
scanf("%ms", &people[i].name);
This changes the expected argument type from char* to char**, it will malloc() a sufficiently sized buffer for you, and store the address at the given location. I.e, this call does precisely what you want it to do. Just don't forget to free() the resulting string once you are done with it.
The "m" modifier works with the "%ms", "%m[" and "%mc" conversion specifiers, i.e. all the conversions that can output a string of unknown length.
(The other allocating input functions that you should keep in mind are getline() and getdelim().)
findname is already a char *.
You ask for reference of this value, it represent char **
scanf("%s", &findName);
Try this :
scanf("%s", findName);
Here is an example with dynamically allocated memory and scanf with %ms:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct
{
char *name;
char *number;
}
person;
int main(void)
{
int n = 0;
char *findName ;
scanf("%i", &n);
if (n == 0) return 1;
person* people = (person*)malloc(n * sizeof(person));
if (people == NULL) return 1;
for(int i = 0; i < n; i++)
{
person p = people[i];
printf("Enter Name and number:\n");
printf("Number: ");
scanf("%ms", &(p.number));
printf("Name: ");
scanf("%ms", &(p.name));
people[i] = p;
}
printf("Enter name to search: ");
scanf("%ms", &findName);
printf("Searching for: %s\n", findName);
for(int i = 0; i < n; i++)
{
person p = people[i];
if(strcmp(p.name, findName) == 0)
{
printf("Number for %s is %s\n", p.name, p.number);
}
}
free(findName);
for(int i = 0; i < n; i++)
{
//notice how we have to free the strings for each name and number,
// and not just the people array
free(people[i].name);
free(people[i].number);
}
free(people);
return 0;
}

C compiler error: undefined reference to function

After I execute the exe I get this error :
undefined reference to `StudentScan'
error: ld returned 1 exit status|
Note: I'm bad and new to coding so don't mind my bad coding please^^
Note2: I'm just messing with random functions.
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
char AverageNum[2];
};
void StudentScan(int, struct student[]);
void StudentPrint(int, struct student[]);
int main() {
int i;
int length;
struct student *studentp;
printf ("\nEnter the host of students: ");
scanf ("%d ", &length);
struct student list[length];
studentp=malloc(length*sizeof(struct student));
if (studentp==NULL)
{
printf("Out of memory!");
return 0;
}
for(i = 0; i < length; i++) {
StudentScan(i,studentp);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
free (studentp);
void StudentScan(int i, struct student list[])
{ printf("\nEnter first name : ");
scanf("%s", list[i].firstName);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
return 0;
}
The posted code has defined StudentScan() within main(). But nested function definitions are not allowed in C. This should generate a compiler warning, such as:
warning: ISO C forbids nested functions [-Wpedantic]
void StudentScan(int i, struct student list[])
Pay attention to all compiler warnings and fix them. If no warning is seen when compiling this code, turn up the level of compiler warnings. On gcc, I suggest to always use at least gcc -Wall -Wextra, and I always add -Wpedantic. The -Wpedantic is needed with gcc to see a warning for this. Some compilers, and gcc is one of these, do support nested function definitions as a compiler extension. Still, this feature is nonstandard, and it is best to not rely on it.
The fix is simple: move the definition of StudentScan() out of main():
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
char AverageNum[2];
};
void StudentScan(int, struct student[]);
void StudentPrint(int, struct student[]);
int main(void) {
int i;
int length;
struct student *studentp;
printf ("\nEnter the host of students: ");
scanf ("%d ", &length);
struct student list[length];
studentp=malloc(length*sizeof(struct student));
if (studentp==NULL)
{
printf("Out of memory!");
return 0;
}
for(i = 0; i < length; i++) {
StudentScan(i,studentp);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
free (studentp);
return 0;
}
void StudentScan(int i, struct student list[])
{ printf("\nEnter first name : ");
scanf("%s", list[i].firstName);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
Also note that you should always specify maximum widths when reading strings using scanf() family functions with %s or %[] to avoid buffer overflow. For example:
scanf("%19s", list[i].firstName);
Note that 19 is used, even though the firstName field is an array of 20 char values. Remember that one space must be reserved for the \0 terminator. And since you are using %s to read a string into the AverageNum field, you should also have:
scanf("%1s", list[i].AverageNum);
That is, this field can only hold one digit. If the intention is to hold two digits, this field must be changed within the struct to: char AverageNum[3].
And while we are discussing scanf(), note that this function returns the number of successful assignments made during the function call. If no assignments are made, 0 is returned. This return value should always be checked. Consider: if the user mistakenly enters a letter when a digit is expected, nothing is stored in the intended variable. This may lead to undefined behavior. You may try something like this to validate numeric input:
printf ("\nEnter the host of students: ");
while (scanf ("%d ", &length) < 1) {
puts("Please enter a number");
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
This code asks the user to enter input again if a number is not entered when expected. Note that if the user does enter a non-digit, this character remains in the input stream and must be cleared before attempting to process more user input. The while loop is a typical construction which accomplishes this task.
Edit
Based on comments made by the OP, here is a modified version of the posted code. This version uses a float value instead of a character array for the AverageNum field of the struct. A floating-point type may be more useful than an integer type for storing averages. It is usually best to use double for floating-point values, but in this case it looks like AverageNum has little need for precision (the char array was intended to hold only two digits); float is probably sufficient for this use. If a different type is desired, it is simple enough to modify the code below.
Some input validation is implemented, but note that more could be done. The user is prompted to enter a number when non-numeric input is found where numeric input is expected. The input stream is cleaned with the while loop construction after such an input mistake; it would be good to remove this code to a separate function called clear_input(), for example.
If the user signals end-of-file from the keyboard, scanf() will return EOF; the code below chooses to exit with an error message rather than continue with malformed input in this case. This could also occur with input redirected from a file, and this condition may need to be handled differently if such input is expected.
The loop that populated the list[] array seemed to be operating inefficiently, asking for AverageNum twice in each pass. This has been streamlined.
Note that the call to malloc() can be rewritten as:
studentp = malloc(length * sizeof *studentp);
This is a very idiomatic way of writing such an allocation. Here, instead of using an explicit type as the operand of sizeof, that is, instead of sizeof (struct student), the variable which holds the address of the allocation is used. sizeof only uses the type of the expression *studentp, so this variable is not dereferenced here. Coding this way is less error-prone and easier to maintain when types change during the maintenance life of the code.
Yet, it is unclear why memory is allocated for studentp in the first place. In the posted code, both the firstName and AverageNum fields are filled for members of the dynamically allocated studentp in calls to StudentScan() in a loop; the same loop fills the AverageNum field of the members of list[] (a different array of structs) with different input. There seems to be no need for one of these arrays of student structs; I have commented-out the dynamically allocated array in favor of the statically allocated version.
Here is the modified code:
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
float AverageNum;
};
void StudentScan(int, struct student[]);
void StudentPrint(int, struct student[]);
int main(void) {
int i;
int length;
// struct student *studentp;
printf ("\nEnter the host of students: ");
while (scanf ("%d", &length) < 1) {
puts("Please enter a number");
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
struct student list[length];
/* This is fine */
// studentp = malloc(length * sizeof (struct student));
/* But this is better */
// studentp = malloc(length * sizeof *studentp);
// if (studentp == NULL)
// {
/* Not wrong, but... */
// printf("Out of memory!");
// return 0;
// fprintf(stderr, "Allocation failure\n");
// exit(EXIT_FAILURE);
// }
for(i = 0; i < length; i++) {
StudentScan(i, list);
}
/* Code to display results here */
// free (studentp);
return 0;
}
void StudentScan(int i, struct student list[])
{
putchar('\n');
printf("Enter first name: ");
if (scanf("%19s", list[i].firstName) != 1) {
puts("Input error");
exit(EXIT_FAILURE);
}
printf("Enter average number: ");
while (scanf("%f", &list[i].AverageNum) < 1) {
puts("Please enter a number");
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
}
You have to remove the scan function from the main. Also there is not a printstudent function you are declaring. You must remove /n from the printf and the scanf functions and place them accordingly. You can then test if your data are being added correctly in your struct with a simple loop.
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
char AverageNum[2];
};
int main() {
int i=0;
int length;
struct student *studentp;
printf ("Enter the host of students:");
scanf ("%d", &length);
struct student list[length];
studentp=malloc(length*sizeof(struct student));
if (studentp==NULL)
{
printf("Out of memory!");
return 0;
}
for(i = 0; i < length; i++) {
printf("Enter first name :");
scanf("%s", list[i].firstName);
printf("Enter average number: ");
scanf("%1s", list[i].AverageNum);
}
for(i = 0; i< length; i++){
printf("number of host is: %d , his/her first name: %s , his/her avg number: %s \n", i, list[i].firstName, list[i].AverageNum);
}
free (studentp);
return 0;
}

s expects argument of type char c but argument 2 has type 'int' warning and bad return

Yes ,I know that this question was already asked for many times ,but none of these helped me to discover the problem (duplicate...yeah). I want to read from input a series of strings into an array and then search from 'First Name'. If the name exist ,I want to display all the data stored in that element of array (I attached the code to undestand easily). When I run it ,I read from keyboard all the data ,but it returns me absolutely nothing.
#include<stdio.h>
typedef struct record {
char name[10],lname[10],phone[10],bday[10];
};
void main() {
struct record rec;
char search;
int i,nr;
printf("\nInput number of records: ");
scanf("%d",&nr);
for (i=0 ; i<nr ;i++) {
printf("First name: ");
scanf("%s",&rec.name[i]);
printf("Last name: ");
scanf("%s",&rec.lname[i]);
printf("Phone: ");
scanf("%s",&rec.phone[i]);
printf("Bday: ");
scanf("%s",&rec.bday[i]);
}
printf("Input the first name for searching: ");
scanf("%s",&search);
for (i=0 ;i<nr;i++) {
if (search == rec.name[i]) {
printf("First name: %s\nLast name: %s\nPhone: %s\nB-day: %s",rec.name[i],rec.lname[i],rec.phone[i],rec.bday[i]);
}
}
}
NOTE: I already replaced
scanf("%s",&rec.name[i]);
with
scanf("%s",rec.name[i]);
but no effect.
I believe there are a lot of problems with your code.
Firstly in this line:
scanf("%s",&search);
You have declared search as only a char, when really you want an array of chars. You also don't need & with search, as an array decays to a pointer to the first element.
It instead should be like this:
char search[10];
scanf("%9s", search); /* %9s to avoid buffer overflow */
You need to make this change to all your other scanf() calls, as this seems to be everywhere in this code.
It also seems that you want to create an array of records(structures), So you might need to make this after getting the value of nr. You can create it like this:
struct record rec[nr]; /* array of nr structures */
This also means calls like this:
rec.name[i]
Don't make sense, as you are iterating over the characters within a name, not over all the records in struct records.
This needs to be instead:
rec[i].name
Secondly, Your using == to compare strings, when you should be using strcmp instead. Using == will only compare the base address of the strings, not the actual contents of strings.
Your line should be this instead:
if (strcmp(search, rec[i].name) == 0) {
If you read the manual page for strcmp(), checking for a return value of 0 means that both strings are equal in comparison.
Lastly, in your first scanf() call:
scanf("%d",&nr);
You should really check the return value of this:
if (scanf("%d", &nr) != 1) {
/* exit program */
}
Note: For reading strings, you should really be using fgets instead. You can try upgrading to this later, but I think it is better to understand these basics first.
Here is working example of what your program should do:
#include <stdio.h>
#include <string.h>
#define STRSIZE 10
typedef struct {
char name[STRSIZE+1]; /* +1 to account for null-btye at the end */
char lname[STRSIZE+1];
char phone[STRSIZE+1];
char bday[STRSIZE+1];
} record;
int main() {
char search[STRSIZE+1];
int i,nr;
printf("\nInput number of records: ");
if (scanf("%d", &nr) != 1) {
printf("Invalid input.\n");
return 1;
}
record rec[nr]; /* array of records */
for (i = 0; i < nr ; i++) {
printf("First name: ");
scanf("%10s", rec[i].name);
printf("Last name: ");
scanf("%10s", rec[i].lname);
printf("Phone: ");
scanf("%10s", rec[i].phone);
printf("Bday: ");
scanf("%10s", rec[i].bday);
}
printf("Input the first name for searching: ");
scanf("%10s", search);
for (i = 0; i < nr; i++) {
if (strcmp(search, rec[i].name) == 0) {
printf("First name: %s\nLast name: %s\nPhone: %s\nB-day: %s\n",rec[i].name,rec[i].lname,rec[i].phone,rec[i].bday);
} else {
printf("Record not found.\n");
}
}
return 0;
}
The numeric input leaves a new line character in the input buffer, which is then picked up by the character input. when numeric input with scanf() skips leading white space, character input does not skip this leading white space.
Use a space before %c and it will help you cause if space is not used then a buffer added with value .so that use space before %c
scanf(" %c",&rec.name[i]);

Find Biggest Number in C, by N number of inputs

So I have this code:
#include <stdio.h>
int main()
{
char peopleName[5][20],peopleAge[5];
int i;
int maxAge=0, maxName=-1;
for(i=0;i<5;i++)
{
printf("Name & Age %d :",i+1);
scanf("%s",&peopleName[i]);
scanf("%d",&peopleAge[i]);
if(peopleAge[i]>maxAge)
{
maxAge=peopleAge[i];
maxName=i;
}
}
printf("%s %d", peopleName[maxName],peopleAge[maxAge]);
}
This code finds from 5 people the oldest one. I want to change from 5 people to N number of people, whatever the number I input myself. (For example I put 7, and I can insert seven names and ages and so on).
The question has two parts: How does the user specify how many persons are entered? And how do I store the data?
The second part is easy: No matter how many persons you are going to consider, if you just want to know who is the oldest, it is enough to keep the name and age of the currently oldest person. (Of course, if there is a tie and many people are, say, 80 years old, you just get to keep the first match.)
Not storing anything also simplifies the first question. You could ask the user to specify the number of persons beforehand and that's find if you have few people. If you have a list of many people, the user would have to count the by hand and then enter the count. Miscounting is very likely.
A better way is to indicate the end of input by another means, for example by a negative age or by two dashes as name. There is also the possibility that the input runs out, for example when redirecting input from a file or when pressing one of Ctrl-Z or Ctrl-D, depending on your platform, after the input.
The example below read the input line-wise and then scans that line. The loop while (1) is in theory infinite, in practice execution breaks out of the loop when the input runs out – fgets return NULL –, when a blank line is read or when the input isn't in the format single-word name and age.
#include <stdio.h>
int main(void)
{
char oldest[80] = "no-one";
int max_age = -1;
int count = 0;
puts("Enter name & age on each line, blank line to stop:");
while (1) {
char line[80];
char name[80];
int age;
if (fgets(line, sizeof(line), stdin) == NULL) break;
if (sscanf(line, "%s %d", name, &age) < 2) break;
if (age > max_age) {
strcpy(oldest, name);
max_age = age;
}
count++;
}
printf("The oldest of these %d people is %s, aged %d.\n",
count, oldest, max_age);
return 0;
}
You can do this -
int n; // number of people
scanf("%d",&n); // take input from user
char peopleName[n][20],peopleAge[n]; // declare 2-d array
for(i=0;i<n;i++)
{
// your code
}
Also this statement -
scanf("%s",&peopleName[i]); // pass char * as argument to %s
should be -
scanf("%19s",peopleName[i]); // one space is left for null character
You can use malloc to allocate buffer dynamically.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char (*peopleName)[20];
int *peopleAge;
int i;
int maxAge=0, maxName=-1;
int dataNum;
printf("How many people? :");
if(scanf("%d",&dataNum)!=1)return 1;
peopleName=malloc(sizeof(char[20])*dataNum);
peopleAge=malloc(sizeof(int)*dataNum);
for(i=0;i<dataNum;i++)
{
printf("Name & Age %d :",i+1);
scanf("%s",peopleName[i]);
scanf("%d",&peopleAge[i]);
if(peopleAge[i]>maxAge)
{
maxAge=peopleAge[i];
maxName=i;
}
}
printf("%s %d", peopleName[maxName],peopleAge[maxName]);
free(peopleName);
free(peopleAge);
return 0;
}
Also please note that:
You should pass char*, not char(*)[20], for %s in scanf
peopleAge[maxAge] may be out of bounds. maxName (or other name but same role) should suit here.

Resources