struct student{
char *number;
char *name;
int semester;
float *grades;
};
typedef struct student RECORD;
void read_record(RECORD *p);
void print_record(RECORD x);
void init_record(RECORD *p);
void free_record(RECORD x);
main()
{
int N;
printf("Please enter the number of students that you want to insert: ");
scanf("%d",&N);
RECORD array[N];
int i;
for (i=0; i<N; i++)
init_record(&array[i]);
at this point, when i put the semester value from the keyboard my program crashes
for (i=0; i<N; i++)
{
printf("Number %d student: \n",i+1);
read_record(&array[i]);
}
for (i=0; i<N; i++)
print_record(array[i]);
for (i=0; i<N; i++)
{
free_record(array[i]);
}
}
I tried to remove any unnecessary code but I left the most important parts so any false part would be easier to be seen
void read_record(RECORD *p)
{
int i;
printf("Please give the name: ");
scanf("%s", p->name);
printf("Please give AM number: ");
scanf("%s", p->number);
printf("Please give semester: ");
scanf("%d",p->semester);
printf("Please give grades: ");
for(i=0;i<5;i++)
scanf("%f", &(p->grades[i]));
}
After this piece of code I have the init_record
function that includes mallocs for struct's arrays
void init_record(RECORD *p)
{
p->name = malloc(sizeof(char)*40);
if (!p->name)
{
printf("Error!");
exit(0);
}
p->number = malloc(sizeof(char)*6);
if (!p->number)
{
printf("Error!");
exit(0);
}
p->grades = malloc(sizeof(float)*5);
if (!p->grades)
{
printf("Error!");
exit(0);
}
}
Assuming that you've allocated the memory for the struct members
printf("Please give semester: ");
scanf("%d",p->semester);
is the problem, as p->semester
is an int
. %d
expects a pointer to int
, you are passing an uninitialized integer value as a pointer to scanf
.
The correct call would be
scanf("%d", &p->semester);
Usually once uses malloc
because one need objects that live outside the scope of function, for example with linked lists, trees, etc. But also when you need an array whose dimension is not known on compiler time.
Your init_record
seems to me pointless, because you know the size of the arrays and they are even small, you could easily change your struct to
struct student {
char number[6];
char name[40];
int semester;
float grades[5];
};
and then you wouldn't need to do the malloc
calls.
Also, you would need to do the scanf
like this:
int c;
scanf("%39s", p->name);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer
scanf("%5s", p->number);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer
...
to prevent buffer overflows if the user enters a name/number that are too large for the buffer.
edit
OP asked in the comments
One last thing: I wanted to replace scanf with
gets
in the case of name, because I want a name and a surname to be saved as one piece(including space). But when I replaced scanf, I got this error: request for member 'name' in something not a structure or union. I searched over here for a similar problem but I didn't find any solution. Can you suggest one?
Don't use gets
, this is a dangerous function because it doesn't take the size of the array into account and if the entered text is larger than the buffer size, it will overflow and cause a lot of damage. The error message is due to a syntax error, as you don't show the code, I cannot say what you did wrong. However, if you take my advice, you won't have this error.
In general I advice not to use scanf
to read from the user, because scanf
was not designed to do so. Specially when you want to read strings that have empty spaces, it's better to read the whole line with fgets
and parse the line later using other functions like strchr
, strstr
, strtok
, sscanf
, strtol
, etc. Which function to use depends on what you are trying to read from the user. In this case where you are reading strings, fgets
would give better results. So I'd change your whole reading process to this:
int read_record(RECORD *p)
{
char line[1024];
printf("Please give the name: ");
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Could not read name\n");
return 0; // error
}
line[strcspn(line, "\n")] = 0; // removing newline
strncpy(p->name, line, sizeof p->name);
p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string
printf("Please give AM number: ");
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Could not read AM number\n");
return 0;
}
line[strcspn(line, "\n")] = 0; // removing newline
strncpy(p->number, line, sizeof p->name);
p->name[sizeof(p->number) - 1] = 0; // making sure to get a valid string
// this is ok, this can stay like this
printf("Please give semester: ");
scanf("%d", &p->semester);
printf("Please give grades: ");
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Could not read grades\n");
return 0;
}
if(sscanf(line, "%f %f %f %f %f",
p->grades, p->grades + 1, p->grades + 2, p->grades + 3,
p->grades + 4) != 5)
{
fprintf(stderr, "Could not read 5 grades\n");
return 0;
}
return 1; // success
}
I know that it is much more code that you have before, but this code is more robust, it handles cases where the user did input the wrong format, your code can react to that, print error messages, retry the user input, whatever. With your the error manifest themselves when the data printed on screen or a file is strange.
Note how I copied the string:
strncpy(p->name, line, sizeof p->name);
p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string
Here I assuming that you've changed the struct to hold arrays like I said in the first part of the answer. If you didn't change that and you are still using the old way by malloc
ing with a hardcoded fixed size, you will have to use the hardcoded fixed size here as well:
strncpy(p->name, line, 40);
p->name[39] = 0; // making sure to get a valid string
And you see why I prefer when the struct has the array, because with sizeof
I can get the size regardless of the dimension.
The other thing to notice here is that I used strncpy
instead of strcpy
. strcpy
suffers from the same problems as gets
, it doesn't take the size of the destination buffer into consideration and if the source string is larger than the destination buffer, it will overflow the buffer.
strncpy
works like strcpy
except that you pass how many bytes there is available for the destination buffer. If the source is larger than that number, then strncpy
will not writing bytes in the destination, thus preventing a buffer overflow. Of course if the '\\0'
-terminating byte is not among the copied bytes, it won't be written in the destination buffer. p->name[39] = 0;
just makes sure that the string is '\\0'
-terminated, no matter how long the source was.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.