I'm not sure how I can store and modify an int using the following stucts in C. I don't think the values that I insert into a student struct are being stored. The student grade must stay void* and I need to be able to access/modify the grade at anytime until exiting.
typedef struct student{
void *grade;
} student;
typedef struct school{
student **students;
size_t school_size;
}school;
student *student_create(int x){
student *new_student = malloc(sizeof(student));
new_student->grade = malloc(sizeof(int));
new_student->grade = &x;
return new_student;
}
school *school_create(size_t school_szie) {
school *s = (school*)malloc(sizeof(school));
s->students = (student**)malloc(sizeof(student*) * school_szie);
for (int i = 0; i < school_szie; i) {
s->students[i] = NULL;
}
return s;
}
void school_insert(school *s, int count, int index){
if(s->students[index] == NULL){
student *new_s = student_create(count);
s->students[index] = new_s;
}
}
void student_inc(school *s, int index){
*(int *)s->students[index]->grade = *(int *)s->students[index]->grade 1;
}
int main(){
int a = 10;
void *ptr = &a;
printf("%d\n", *(int *)ptr);
*(int *)ptr = *(int*)ptr 1;
printf("%d\n", *(int *)ptr);
school *s1 = school_create(5);
school_insert(s1, 2, 1); // school name, grade, index
school_insert(s1, 4, 0);
school_insert(s1, 7, 3);
printf("%d\n", *(int *)s1->students[1]->grade);
printf("%d\n", *(int *)s1->students[0]->grade);
printf("%d\n", *(int *)s1->students[3]->grade);
return 0;
}
I'm getting the following output,although the intended output of the last three lines should be 2, 4, 7.
10
11
7
7
7
CodePudding user response:
There are several issues. Look at the commented lines.
#include <stdio.h>
#include <stdlib.h>
typedef struct student {
void* grade;
} student;
typedef struct school {
student** students;
size_t school_size;
}school;
student* student_create(int x) {
student* new_student = malloc(sizeof(student));
new_student->grade = malloc(sizeof(int));
*(int*)(new_student->grade) = x; // changed
return new_student;
}
school* school_create(size_t school_szie) {
school* s = (school*)malloc(sizeof(school));
s->students = (student**)malloc(sizeof(student*) * school_szie);
for (size_t i = 0; i < school_szie; i) {
s->students[i] = NULL;
}
s->school_size = school_szie; // you forgot this
return s;
}
void school_insert(school* s, int count, int index) {
if (s->students[index] == NULL) {
student* new_s = student_create(count);
s->students[index] = new_s;
}
}
void student_inc(school* s, int index) {
*(int*)s->students[index]->grade = *(int*)s->students[index]->grade 1;
}
int main() {
int a = 10;
void* ptr = &a;
printf("%d\n", *(int*)ptr);
*(int*)ptr = *(int*)ptr 1;
printf("%d\n", *(int*)ptr);
school* s1 = school_create(5);
school_insert(s1, 2, 1); // school name, grade, index
school_insert(s1, 4, 0);
school_insert(s1, 7, 3);
printf("%d\n", *(int*)(s1->students[1]->grade)); // changed
printf("%d\n", *(int*)(s1->students[0]->grade)); // changed
printf("%d\n", *(int*)(s1->students[3]->grade)); // changed
return 0;
}
This doesn't make sens for two reasons:
new_student->grade = malloc(sizeof(int));
new_student->grade = &x;
new_student->grade = &xoverwritesnew_student->gradethat has been allocated on the previous linenew_student->grade = &xputs the pointer to the local variablexintonew_student->grade, but this local variable will cease to exist once thestudent_createfunction is over.
You want: *(int*)(new_student->grade) = x;. You've done it right with your tests using a and ptr at the beginning of main.
You forgot to assign the school size in school_create
s->school_size = school_szie; // you forgot this
This is wrong:
printf("%d\n", s1->students[1]->grade);
s1->students[1]->grade is the pointer to the grade, but you want the grade itself. Here you pretend s1->students[1]->grade is actually an int, and printf prints this as if it was an int. This is undefined bahaviour, the value printed can be any garbage value.
Therefore you need *(int *)s1->students[1]->grade. (int *) casts the pointer to void to a pointer to int, and the * dereferences the pointer to int.
Finally you should use size_t isntead of int in this for loop:
for (size_t i = 0; i < school_szie; i)
school_szie being a size_t.
This last problem however has no impact on the program output here.
That being said, be aware that having void *grade in struct student looks like very, very poor (if not worse) design to me. It should be int grade. You want to store a grade.
Also struct student should probably contain at least a student name or some student identifier.
CodePudding user response:
I compiled your program with gcc -g main.c -o main and ran it with valgrind ./main to find memory issues quickly. Try it!
Valgrind complains about conditional jumps depending on uninitialized values for the last 3 printf statements. That means the 3 grades aren't initialized.
Though it took some digging to find the problem. It's in:
student *student_create(int x){
student *new_student = malloc(sizeof(student));
new_student->grade = malloc(sizeof(int));
new_student->grade = &x;
return new_student;
}
The new_student->grade = &x; line is setting grade to be a pointer to the argument x, which will be completely invalid when the function returns! You really want *(int*)new_student->grade = x, which writes x to the newly-allocated memory location.
CodePudding user response:
Well this doesn't make much sense:
new_student->grade = malloc(sizeof(int));
new_student->grade = &x;
Memory is allocated and assigned to new_student->grade, then that is promptly overwritten by a pointer to a parameter. So you have now a memory leak (you can never again free the allocated memory) and undefined behaviour as that pointer to a local variable is no longer valid after the function returns.
What you want to do is allocate an integer, assign it a value, and then assign the pointer to it to new_student->grade.
int *grade = malloc(sizeof *grade);
if (!grade) {
// handle the error properly (not shown here)
return NULL;
}
*grade = x;
new_student->grade = grade;
