在C中,如果struct变量里面有一个指针,它是否需要是指针?

In C, does struct variable needs to be pointer if there is a pointer inside it?

在我的代码中有这个(练习给出):

typedef struct student{
    int id;
    float *grades;
    char name[100];
}Student;

Student* fillStudent();

我的问题是:为什么函数“fillStudent()”return是一个 Student 类型的指针?是因为里面有等级指针吗?最初我认为这是因为你应该 return 各种学生的向量,但它没有意义,因为另一个结构:

typedef struct subject{
    Student *V;
    float average[5];
    int nStudents; 
}Subject;

练习要求您调用函数“fillStudent()”nStudents 次来填充向量 V,因此 return all 没有意义学生在一次通话中。 那么为什么 fillStudent() 需要 return 一个指针呢?难道它不能只是一个 Student 类型的变量,并在成绩上做一个 malloc 吗?如果是这样,那么这个变量最终会被认为是一个指针吗?

只能告诉你,你说的很有道理。首先,一个名为 fillStudent 的函数应该“填充”已经存在的东西。因此,可能的签名是:

void fillStudent(Student *dest);

这将是理想的,因为您会预先分配 nStudents,然后调用函数 fillStudent nStudents 次。

此外,结构 Student 不是很有用,因为你有一个指针 grades 但你没有变量来知道那里存储了多少成绩(所以我想你可以不要在没有某种约定的情况下循环遍历它们,例如“最后一年级是 -1”,这是一种不当行为。

对于你的问题,在 C 中按值 return struct 没有问题,即使它们内部有向量。

我想首先解决您在推测推理时提出的一些子问题。

问题1,函数fillStudent() return是不是Student类型的指针,因为里面有一个成绩指针?

答:不。结构可以[通常]在其字段中包含任何类型的变量,而不必在指针变量本身中,并且是正确的 C 代码。

Q2:为什么fillStudent()需要return一个指针?它不能简单地是一个 Student 类型的变量,并在成绩上调用 malloc 吗?

它绝对可以 return 一个 Student 结构,它的成绩字段初始化为 malloc。 (尽管正如@Zeppe 指出的那样,如果您不知道数组的长度,这样做可能会出现问题)。这看起来像这样,

#include <string.h>

Student createStudent(char* name, id, int num_of_grades) {
    Student s;
    s.id = id;
    //since s.name is a fixed length array in the struct, memory was reserved
    //for it when we declared the variable s. 
    strcpy(s.name, name)
    //s.grades is a pointer to memory on the heap, so we have to 
    //allocate with something like malloc or calloc
    s.grades = malloc(num_of_grades * sizeof(float));
    
    return s; //perfectly okay to return a struct from a function.
}

Q3: Q2 中函数编辑的变量return算指针吗?

答:没有。我们 returned 是一个结构而不是指针。 return 是一个指针的函数看起来像这样,

Student * foo() {
    Student * s = malloc(sizeof(Student));

    //code to initialize/do something with s
   
    //s is a pointer to a Student structure, so by returning s, we are returning
    //a pointer to a Student structure
    return s;
}

总而言之,没有理由给出您描述的设置,其中函数 必须 签名 return 一个指针,它甚至可能是一个错误!但是按照他们的方式做并没有错,并且根据其余代码的样子,可能会有一些优点。

第一个是平凡的。如果您的所有其余代码都在处理 Student* 而不是 Student,那么让您用来构造它们的函数给您 Student*' 可能会感觉更方便s 而不是 Student 的。您可以就这是否是一个充分的理由发表意见,但这是我的猜测,最有可能的原因是在您的案例中以这种方式编写。

下一个原因优势更明显。如果创建 Student 可能会失败(比如他们可能没有注册足够的 类 或其他东西),那么您希望能够向程序的其余部分指出这一点。如果您只是 return 一个结构,那么您通常无法从 return 值知道它是否有效。你可以这样做

#include <stdbool.h>
//value of is_error gets set to true if there was an error, and false otherwise
Student createsStudent(char * name, /* other fields */, bool * is_error)

可以像

一样使用
bool is_error;
Student s = createsStudent(/* ... */, &is_error)
if(is_error) {
    //respond to error
}

效果很好,但您最终可能会得到很多额外的 is_error 变量,这些变量可能略微 annoying/distracting。相反,如果创建 Student return 的方法是指向 Student 的指针,那么您可以检查它是否为空,并使用它来了解是否有错误。

最后,如果您有一堆修改 Student 的函数,您可能希望在同一个变量上一个接一个地调用它们。但是如果这样写,

Student createStudent(/*...*?);
void foo(Student* s);
void bar(Student* s);
void baz(Student* s);

你必须这样称呼他们

Student s = createStudent(/*...*/);
foo(&s);
bar(&s);
baz(&s);

但是如果你这样写,函数 return 指向它们参数的指针(它们只是 return s),

Student * createStudent(/*...*/?);
//returns s
Student * foo(Student* s);
//returns s
Student * bar(Student* s);
//returns s
Student * baz(Student* s);

你可以做到,

Student * s = baz( bar( foo( createStudent(/*...*/) ) ) );

这是一件小事,但真的很不错。

总而言之,没有对错之分。