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 = &x
overwritesnew_student->grade
that has been allocated on the previous linenew_student->grade = &x
puts the pointer to the local variablex
intonew_student->grade
, but this local variable will cease to exist once thestudent_create
function 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;