C:通过空指针引用结构体的正确方法

时间:2021-02-27 19:56:57

I'm trying to insert an element of type Student (definition below) into the field "info" of an structure of type "NoArv" (definition below). I NEED to do this through a pointer o type "Base", which is a void-pointer.

我试图将Student类型的元素(定义如下)插入到NoArv类型结构的“info”字段中(定义如下)。我需要通过一个指针o类型的“Base”来实现这一点,它是一个void指针。

I try the code below, but when compiling, I receive the following error message:

我尝试下面的代码,但是在编译时,我收到以下错误消息:

> base.c:38:19: error: dereferencing ‘void *’ pointer [-Werror]
>
     if(a.ra < (*p)->info.ra)
>                    ^
> base.c:38:19: error: request for member ‘info’ in something not a structure or union
> 
> base.c:39:16: error: dereferencing ‘void *’ pointer [-Werror]
>
       *p = (*p)->left;
>
>                 ^

How can I correctly dereference such a pointer to a void type?

如何正确地取消对void类型的指针的引用?

I call the function with this: InsereBase(&base, student)

我用这个函数调用这个函数:InsereBase(&base,学生)

Where "base" is a variable of type Base and "student" is an element of type Student.

“base”是类型基础的变量,“student”是类型student的一个元素。

CODE:

代码:

Boolean InsertBase(Base *p, Student a) {
  while((*p) != NULL){
    if(a.ra < (*p)->info.ra)
      *p = (*p)->left;
    else if(a.ra > (*p)->info.ra)
      *p = (*p)->right;
    else
      return false;
  }

  *p = MALLOC(sizeof(NoArv));
  (*p)->info = MALLOC(sizeof(Student));
  (*p)->info.ra = a.ra;
  (*p)->info.name = a.name;

  (*p)->left = (*p)->right = NULL;

  return true;

}

.

typedef void * Base; 
typedef char * String;
typedef enum {false, true} Boolean;

.

typedef struct {
  int ra;
  String name;
  } Student;

.

typedef struct AuxNoArv {
  Student info;                   
  struct AuxNoArv *left,*right;  
  } NoArv, * ImplBase;

2 个解决方案

#1


2  

First of all, you've typedefd Base as void *, so a function argument of type Base * is not a void * - it's a void **. void * is a generic pointer, but void ** is not, so you'll want to fix that.

首先,您将typedefd基设为void *,所以Base *类型的函数参数不是void *——而是void *。void *是一个通用指针,而void *不是,所以您需要修复它。

Second, you have to cast or otherwise convert a void * to the appropriate type before dereferencing it, like so:

第二,在取消引用之前,您必须将一个void *转换为适当的类型,如下所示:

#include <stdio.h>

typedef void * Base; 
typedef char * String;

typedef struct {
    int ra;
    String name;
} Student;

void change_student(Base p)
{
    Student * s = p;
    s->ra = 8;
    s->name = "Legs on a spider";
}

void print_student(Base p)
{
    Student * s = p;
    printf("%d, %s\n", s->ra, s->name);
}

int main(void)
{
    Student my_student = {42, "Meaning of life"};
    print_student(&my_student);
    change_student(&my_student);
    print_student(&my_student);

    return 0;
}

which outputs:

输出:

paul@horus:~/src/sandbox$ ./void
42, Meaning of life
8, Legs on a spider
paul@horus:~/src/sandbox$ 

If you need pointers to pointers, for instance to malloc() some memory for a node as you suggest in your comments, then something like Student ** can be converted to void * just as well as Student * can, like so:

如果您需要指向指针的指针,例如指向malloc()的指针,如您在注释中建议的某个节点的内存,则可以将Student **之类的内容转换为void *,就像Student *可以转换为Student *一样,如下所示:

#include <stdio.h>
#include <stdlib.h>

typedef void * Base; 
typedef char * String;

typedef struct {
    int ra;
    String name;
} Student;

void change_student(Base p)
{
    Student * s = *((Student **) p);
    if ( s ) {
        s->ra = 8;
        s->name = "Legs on a spider";
    }
    else {
        Student * new_student = malloc(sizeof *new_student);
        if ( !new_student ) {
            perror("couldn't allocate memory");
            exit(EXIT_FAILURE);
        }
        new_student->ra = 4;
        new_student->name = "Horsemen of the Apocalypse";
        *((Student **) p) = new_student;
    }
}

void print_student(Base p)
{
    Student * s = p;
    printf("%d, %s\n", s->ra, s->name);
}

int main(void)
{
    Student my_student = {42, "Meaning of life"};
    Student * pstudent = &my_student;
    print_student(pstudent);
    change_student(&pstudent);
    print_student(pstudent);

    Student * nstudent = NULL;
    change_student(&nstudent);
    print_student(nstudent);

    free(nstudent);

    return 0;
}

yielding:

收益率:

paul@horus:~/src/sandbox$ ./void2
42, Meaning of life
8, Legs on a spider
4, Horsemen of the Apocalypse
paul@horus:~/src/sandbox$ 

#2


0  

I used Paul Griffiths idea and get this code. But that is NOT working. As Griffiths noted in comments, it is just leaking memory.

我用了保罗·格里菲思的想法,得到了这个代码。但这是行不通的。正如格里菲斯在评论中指出的那样,这只是内存泄漏。

Boolean InsertBase(Base *p, Student a) {

  ImplBase s = *p;

  while(s != NULL){
    if(a.ra < s->info.ra)
      s = s->left;
    else if(a.ra > s->info.ra)
      s = s->right;
    else
      return false;
  }

  s = MALLOC(sizeof(NoArv));
  s->info.ra = a.ra;
  s->info.name = a.name;

  s->left = s->right = NULL;

  return true;

}

#1


2  

First of all, you've typedefd Base as void *, so a function argument of type Base * is not a void * - it's a void **. void * is a generic pointer, but void ** is not, so you'll want to fix that.

首先,您将typedefd基设为void *,所以Base *类型的函数参数不是void *——而是void *。void *是一个通用指针,而void *不是,所以您需要修复它。

Second, you have to cast or otherwise convert a void * to the appropriate type before dereferencing it, like so:

第二,在取消引用之前,您必须将一个void *转换为适当的类型,如下所示:

#include <stdio.h>

typedef void * Base; 
typedef char * String;

typedef struct {
    int ra;
    String name;
} Student;

void change_student(Base p)
{
    Student * s = p;
    s->ra = 8;
    s->name = "Legs on a spider";
}

void print_student(Base p)
{
    Student * s = p;
    printf("%d, %s\n", s->ra, s->name);
}

int main(void)
{
    Student my_student = {42, "Meaning of life"};
    print_student(&my_student);
    change_student(&my_student);
    print_student(&my_student);

    return 0;
}

which outputs:

输出:

paul@horus:~/src/sandbox$ ./void
42, Meaning of life
8, Legs on a spider
paul@horus:~/src/sandbox$ 

If you need pointers to pointers, for instance to malloc() some memory for a node as you suggest in your comments, then something like Student ** can be converted to void * just as well as Student * can, like so:

如果您需要指向指针的指针,例如指向malloc()的指针,如您在注释中建议的某个节点的内存,则可以将Student **之类的内容转换为void *,就像Student *可以转换为Student *一样,如下所示:

#include <stdio.h>
#include <stdlib.h>

typedef void * Base; 
typedef char * String;

typedef struct {
    int ra;
    String name;
} Student;

void change_student(Base p)
{
    Student * s = *((Student **) p);
    if ( s ) {
        s->ra = 8;
        s->name = "Legs on a spider";
    }
    else {
        Student * new_student = malloc(sizeof *new_student);
        if ( !new_student ) {
            perror("couldn't allocate memory");
            exit(EXIT_FAILURE);
        }
        new_student->ra = 4;
        new_student->name = "Horsemen of the Apocalypse";
        *((Student **) p) = new_student;
    }
}

void print_student(Base p)
{
    Student * s = p;
    printf("%d, %s\n", s->ra, s->name);
}

int main(void)
{
    Student my_student = {42, "Meaning of life"};
    Student * pstudent = &my_student;
    print_student(pstudent);
    change_student(&pstudent);
    print_student(pstudent);

    Student * nstudent = NULL;
    change_student(&nstudent);
    print_student(nstudent);

    free(nstudent);

    return 0;
}

yielding:

收益率:

paul@horus:~/src/sandbox$ ./void2
42, Meaning of life
8, Legs on a spider
4, Horsemen of the Apocalypse
paul@horus:~/src/sandbox$ 

#2


0  

I used Paul Griffiths idea and get this code. But that is NOT working. As Griffiths noted in comments, it is just leaking memory.

我用了保罗·格里菲思的想法,得到了这个代码。但这是行不通的。正如格里菲斯在评论中指出的那样,这只是内存泄漏。

Boolean InsertBase(Base *p, Student a) {

  ImplBase s = *p;

  while(s != NULL){
    if(a.ra < s->info.ra)
      s = s->left;
    else if(a.ra > s->info.ra)
      s = s->right;
    else
      return false;
  }

  s = MALLOC(sizeof(NoArv));
  s->info.ra = a.ra;
  s->info.name = a.name;

  s->left = s->right = NULL;

  return true;

}