c - 函数在C中返回一个指针
问题描述
我是 C 新手,我尝试创建一个返回指针的函数。我使用了不同的方法来做到这一点:
1.
typedef struct student{
int age;
}student;
student *create_student(){
student* s;
s-> age= 24;
return s;
}
int main() {
student* s = create_student();
printf("the student age is %d", s->age);
return 0;
}
它编译但似乎不起作用。
2.
typedef struct student{
int age;
}student;
student *create_student(){
student* s;
student s2;
s2.age = 24;
s = &s2;
return s;
}
int main() {
student* s = create_student();
printf("the student age is %d", s->age);
return 0;
}
它似乎有效,并打印“学生年龄为 24 岁”,但如果我在之前的 printf 之前添加了一条 printf 语句:
int main() {
student* s = create_student();
printf("This is a test\n");
printf("the student age is %d", s->age);
return 0;
}
它给了我:
这是一个测验
学生年龄是-1422892954
3.
如果我使用以下方式:
typedef struct student{
int age;
}student;
student *create_student(){
student* s = malloc(sizeof(student));
s -> age = 24;
return s;
}
int main() {
student* s = create_student();
// printf("This is a test\n");
printf("the student age is %d", s->age);
return 0;
}
它适用于两种情况,有和没有注释的 printf
我只想知道它对 1 和 2 失败的原因是什么。为什么它对 3 有效?一般来说,什么时候应该使用 malloc,什么时候不应该?
谢谢
解决方案
示例 1
您的示例 1 不起作用,因为没有student
创建任何对象。
student* s;
这将创建一个应该指向 a但当前指向未知内存位置的指针s
,因为它是一个未初始化的变量。它绝对不是指向一个新学生,因为到目前为止还没有创建。student
s->age = 24;
然后,这会写入s
当前指向的未知内存位置,从而破坏进程中的内存。您现在进入了未定义行为 (UB)的领域。您的进程可能会在此时或稍后崩溃,或者它可能会做一些疯狂和意想不到的事情。
考虑在此之后会发生什么是没有意义的,因为您的进程现在已经注定了,需要终止。
示例 2
您的示例 2 可以工作,但仅在某些时候有效,因为 UB 再次发挥作用。
student s2;
在这里,您将 a 创建student
为局部变量。它可能是在堆栈上创建的。在您离开函数之前,该变量一直有效create_student
。
但是,您随后将创建一个指向该student
对象的指针并从您的函数中返回它。这意味着,外部代码现在有一个指向student
对象曾经所在位置的指针,但由于您从函数返回并且它是一个局部变量,它不再存在!有点,就是这样。这是一个僵尸。或者,更好的解释是,就像您删除硬盘上的文件一样——只要没有其他文件覆盖它在磁盘上的位置,您仍然可以恢复它。因此,幸运的是,您age
即使在create_student
返回后也能阅读其中的内容。但是,一旦您稍微更改了场景(通过插入另一个printf
),您就走运了,并且该printf
调用使用了自己的局部变量student
堆栈上的对象。哎呀。这是因为使用指向不再存在的对象的指针也是未定义行为 (UB)。
示例 3
这个例子有效。它是稳定的,它没有未定义的行为(几乎 - 见下文)。那是因为您student
在堆而不是堆栈上创建了malloc
. 这意味着它现在永远存在(或直到您调用free
它),并且在您的函数返回时不会被丢弃。因此,传递一个指向它的指针并从另一个地方访问它是有效的。
只是一个小问题——如果malloc
失败了怎么办,例如你的内存不足?在那种情况下,我们又遇到了一个问题。所以你应该添加一个检查是否malloc
返回NULL
并以某种方式处理错误。否则,s->age = 24
将尝试取消引用一个再次不起作用的空指针。
但是,您也应该在使用完它时记住free
它,否则您会出现内存泄漏。在你这样做之后,请记住现在你的指针确实变得无效并且你不能再使用它,否则我们又回到了 UB 世界。
至于您何时使用的问题malloc
:基本上每当您需要在离开当前范围后创建可以继续存在的东西时,或者当某些东西是本地的但必须相当大(因为堆栈空间有限)时,或者当某些东西必须是时可变大小(因为您可以将所需大小作为参数传递给malloc
)。
最后要注意的一件事:您的示例 3 有效,因为您student
只有一个字段,并且您在再次阅读之前age
将该字段初始化为。24
这意味着所有字段(因为它只有一个)都已初始化。如果它有另一个字段(例如,name
)并且您没有初始化那个字段,那么如果您在其他地方的代码试图读取那个 uninitialized ,那么您仍然会携带一个“UB 定时炸弹” name
。因此,请始终确保所有字段都已初始化。您还可以使用calloc
而不是在malloc
将内存传递给您之前将其填充为零,然后您可以确定您有一个可预测的状态并且它不再是未定义的。
推荐阅读
- python - 如何重定向到 Django 中明确命名的 url?
- ios - 如何快速创建一个获取请求谓词,其中不包括来自相关实体的具有给定属性的项目
- python-3.x - 在迁移时我为 django ModuleNotFoundError 安装了 allauth
- c++ - 表达式必须是可修改的左值。码学院
- c# - C# HttpClient PostAsync 403 被 SSL 禁止
- python - 计算python pandas中范围突破的平均真实范围?
- android - 在 Android Studio 中处理位图的问题
- android - 加密共享偏好数据
- python - skimage profile_line 没有意义
- listview - 如果您已经有列表视图构建器,如何添加可重新排序的列表视图