在C中,如果结构变量内部有指针,它是不是需要指针?
Posted
技术标签:
【中文标题】在C中,如果结构变量内部有指针,它是不是需要指针?【英文标题】:In C, does struct variable needs to be pointer if there is a pointer inside it?在C中,如果结构变量内部有指针,它是否需要指针? 【发布时间】:2022-01-11 03:36:45 【问题描述】:在我的代码中我有这个(由练习给出):
typedef struct student
int id;
float *grades;
char name[100];
Student;
Student* fillStudent();
我的问题是:为什么函数“fillStudent()”返回一个Student类型的指针?是不是因为里面有等级指针?最初我认为这是因为您应该返回一个包含各种学生的向量,但这没有意义,因为另一个结构:
typedef struct subject
Student *V;
float average[5];
int nStudents;
Subject;
该练习要求您调用函数“fillStudent()”nStudents 次来填充向量 V,因此在一次调用中返回所有学生是没有意义的。 那么为什么 fillStudent() 需要返回一个指针呢?难道它不能简单地是一个 Student 类型的变量,并在成绩上做一个 malloc 吗?如果是这样,那么变量到底会被认为是指针吗?
【问题讨论】:
这个练习似乎是由不太熟悉 C 编程的人创建的,因此很难说出他们的想法。 没有技术原因返回一个指针。也许吧,但这只是一个猜测,它旨在提醒用户该结构是动态分配的,使用后需要free()
d。但是,仅从函数签名中无法判断,也无法判断需要如何处理 V
指针。查看函数文档。如果没有,代码不好,不应该作为学习的例子。
是的,这个练习看起来很奇怪,还有很多其他原因。谢谢你们俩!
在 1970 年代和 1980 年代初期,一些编译器不支持按值返回结构;所以返回指针的版本是标准做法。而在 C 语言中,你会发现旧编码器和旧代码存在很多“惯性”;许多人仍然使用 malloc(另一个 ANSI 之前的习惯),并且许多人不知道 22 年前添加的数组类型的更改等。
【参考方案1】:
我想首先解决您在推测推理时提出的一些子问题。
问题一,函数fillStudent()
返回一个Student类型的指针是因为里面有一个成绩指针吗?
答:不。一个结构[通常]可以在其字段中包含任何类型的变量,而不必位于指针变量本身中,并且是正确的 C 代码。
Q2:为什么fillStudent()
需要返回一个指针?难道不能简单地是一个Student类型的变量,并在成绩上调用malloc
吗?
它绝对可以返回一个Student
结构,它的grades 字段用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中函数返回的变量算指针吗?
答:没有。我们返回了一个结构而不是一个指针。返回指针的函数看起来像这样,
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;
总而言之,鉴于您描述的设置,函数 必须 签名返回一个指针,这甚至可能是一个错误!但是按照他们的方式执行它并没有错,并且根据其余代码的外观,可能会有一些优势。
第一个是平凡的。如果您的所有其余代码都在处理Student*
而不是Student
,那么让您用来构造它们的函数为您提供Student*
而不是Student
可能会感觉更方便的。你可以对这是否是一个很好的理由有意见,但我猜测你的情况最有可能是这样写的。
下一个原因有更明显的优势。如果创建Student
可能会失败(比如他们可能没有注册足够的课程或其他东西),那么您希望能够向程序的其余部分表明这一点。如果你只是返回一个结构,那么你通常无法从返回值中知道它是否有效。你可以这样做
#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
变量,这可能会有点烦人/分散注意力。相反,如果创建Student
的方法返回一个指向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);
但是如果你写这个,函数返回指向其参数的指针(它们只是返回 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(/*...*/) ) ) );
这是一件小事,但可以非常好。
总而言之,这两种方法都没有对错。
【讨论】:
很棒的答案,你和 Zeppe 让我对这个概念有了更多的理解。但老实说,我只是认为这是练习的错误。过去有一些像这样奇怪的东西的练习(我在大学上 C 课),所以我想这只是其中的另一个......感谢您花时间解释它:) 您可能是对的,如果这是一个错误,我不会感到惊讶。但是有正当理由返回指针而不是返回普通结构,我想说明这些。特别是,标准库大量使用了错误返回空值和函数链接模式,很高兴知道为什么要这样写。没问题:)【参考方案2】:我只能告诉你,你的推理是有道理的。首先,一个名为fillStudent
的函数应该“填充”已经存在的东西。因此,可能的签名是:
void fillStudent(Student *dest);
这将是理想的,因为您会预先分配 nStudents
,然后调用函数 fillStudent
nStudents
次。
另外,结构 Student
不是很有用,因为你有一个指针 grades
但你没有变量来知道那里存储了多少成绩(所以我猜你不能循环遍历它们某些类型的约定,例如“最后一个年级是-1
”,这是一种不当行为。
对于您的问题,C 中按值返回 struct
没有问题,即使它们里面有一个向量。
【讨论】:
以上是关于在C中,如果结构变量内部有指针,它是不是需要指针?的主要内容,如果未能解决你的问题,请参考以下文章