繁体   English   中英

Function 返回 C 中的指针

[英]Function return a pointer in C

我是 C 的新手,我尝试创建一个 function 返回一个指针。 我使用了不同的方法来做到这一点:

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 object。

student* s;

这将创建一个指针s ,它应该指向一个student ,但当前指向一个未知的 memory 位置,因为它是一个未初始化的变量。 它绝对不是指向一个新学生,因为到目前为止还没有创建。

s->age = 24;

然后,这将写入s当前指向的未知 memory 位置,在此过程中破坏 memory。 您现在输入了未定义行为 (UB)的 realm 。 您的进程可能会在此时或稍后崩溃,或者它可能会做一些疯狂和意想不到的事情。

考虑在此之后会发生什么是没有意义的,因为您的进程现在已经注定了,需要终止。

示例 2

您的示例 2 可以工作,但仅在某些时候有效,因为 UB 再次发挥作用。

student s2;

在这里,您正在创建一个student作为局部变量。 它可能是在堆栈上创建的。 在您离开 function create_student之前,该变量一直有效。

但是,您将创建一个指向该student object 的指针,并从您的 function 返回它 这意味着,外部代码现在有一个指向student object 曾经所在的位置的指针,但是由于您从 function 返回并且它是一个局部变量,它不再存在,排序。 那是。 这是一个僵尸,或者,更好的解释是,就像您删除硬盘上的一个文件 - 只要没有其他文件覆盖它在磁盘上的位置。 您仍然可以恢复它,因此,幸运的是,即使在create_student返回之后,您也可以读取它的age 但是,一旦您稍微更改场景(通过插入另一个printf ),您就会运气不佳,并且printf调用使用其自己的局部变量,这些变量会覆盖堆栈上的student object。 哎呀。 这是因为使用指向不再存在的 object 的指针也是未定义行为 (UB)。

示例 3

这个例子有效。 它是稳定的,它没有未定义的行为(几乎 - 见下文)。 那是因为您使用malloc而不是堆栈上创建student 这意味着它现在永远存在(或者直到你free调用它),并且当你的 function 返回时不会被丢弃。 因此,传递一个指向它的指针并从另一个地方访问它是有效的。

只是一个小问题——如果malloc失败了怎么办,例如你用完了 memory? 在那种情况下,我们又遇到了问题。 因此,您应该检查malloc是否返回NULL并以某种方式处理错误。 否则, s->age = 24将尝试取消引用 null 指针,该指针再次不起作用。

但是,您还应该记住在使用完毕后将其free ,否则您会出现 memory 泄漏。 在你这样做之后,请记住现在你的指针确实变得无效并且你不能再使用它,否则我们又回到了 UB 世界。

至于您何时使用malloc的问题:基本上每当您需要在离开当前 scope 后创建可以继续存在的东西时,或者当某些东西是本地的但必须很大(因为堆栈空间有限)时,或者当某些东西必须大小可变(因为您可以将所需的大小作为参数传递给malloc )。

最后要注意的一件事:您的示例 3 有效,因为您的student只有一个字段age ,并且您在再次阅读之前将该字段初始化为24 这意味着所有字段(因为它只有一个)都已初始化。 如果它有另一个字段(例如name )并且您没有初始化那个字段,如果您在其他地方的代码试图读取那个未初始化的name ,您仍然会携带一个“UB 定时炸弹”。 因此,请始终确保所有字段都已初始化。 您还可以使用calloc而不是malloc来让 memory 在传递给您之前用零填充,然后您可以确定您有一个可预测的 state 并且它不再未定义。

因为在 case1 和 2 中,变量“age”在子函数 create_student() 的 scope 中,在堆栈上。 所以一旦子功能完成,那个区域就被释放了,也就意味着“年龄”也被释放了。 所以 s 现在指向一个无意义的区域。 如果你足够幸运,该区域仍然存储“年龄”,你可以打印出该信息,这就是它在 case2 的第一部分工作的原因。

但在 case3 中,student* s 指向一个堆区域,当该子功能完成时,该堆区域将不再空闲。 所以 s->age 仍然有效。

继续评论,你可以这样想:

情况1。

    student* s;

s在未初始化的指针中,它不指向您可以有效使用的任何 memory。 它的价值是不确定的。

案例 2。

    student* s;
    student s2;
    s2.age = 24;
    s = &s2;

s是一个未初始化的指针(如 1.), s2声明一个有效的结构并且s2.age被有效地初始化。 s = &s2; s2的地址(声明为函数的本地)分配给s 返回s时, s2无效——它的寿命仅限于 function,因此返回到main()的地址无效。

案例 3。

    student* s = malloc(sizeof(student));

是的!! s现在保存 memory 的有效块的起始地址,该块具有分配的存储持续时间(有利于程序的生命周期或直到被释放)。 唯一的问题是您需要验证分配是否成功:

    if (s == NULL) {
        perror ("malloc-s");
        return NULL;
    }

现在您可以确信返回是有效地址或NULL指示分配失败。

何时使用

对于最后一个问题,当您不知道需要多少东西时,您会动态分配,因此您声明其中的一些,跟踪您使用了多少,并在最初分配的块被填充时realloc更多。 或者,您需要比程序堆栈更多的东西,您可以分配或声明为static (或全局声明)。 否则,如果您事先知道需要多少,并且可以放入堆栈,只需声明它们的数组即可。

如果您还有其他问题,请告诉我。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM