Home > Blockchain >  Change the value of pointed to by a void pointer
Change the value of pointed to by a void pointer

Time:09-17

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;
  1. new_student->grade = &x overwrites new_student->grade that has been allocated on the previous line
  2. new_student->grade = &x puts the pointer to the local variable x into new_student->grade, but this local variable will cease to exist once the student_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;
  • Related