Reputation: 23
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;
}
Upvotes: 2
Views: 1925
Reputation: 21324
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.
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 struct
s) with different input. There seems to be no need for one of these arrays of student
struct
s; 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;
}
}
}
Upvotes: 1
Reputation: 337
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;
}
Upvotes: 0