从函数中返回此指针

37
我试图从一个函数中返回指针,但是我得到了一个分段错误。请问代码有什么问题?
#include <stdio.h>

int *fun();

main()
{
    int *ptr;
    ptr = fun();
    printf("%d", *ptr);
}

int *fun()
{
    int *point;
    *point = 12;
    return point;
}

14
处理指针时最重要的问题是:指向_什么东西_?本地对象?炸了。动态分配的对象?由谁释放?某个存储在其他位置的对象?那么该对象存在多长时间,我的指针有效期有多长?从函数返回指针尤其具有风险,因为指针是在完全不同的代码中初始化的(通常对调用者不可见),调用者不知道如何处理结果。对于这样的函数,良好的文档非常重要。 - sbi
2
请记住,始终要为任何对象、指针和数据结构分配内存空间。如果不这样做,您将始终收到分段错误的提示,因为它只是告诉我们没有为您分配任何空间。 - Kevin
1
当你“更改代码中的错误”时,你会使答案(部分)与问题不相关。问题中的代码并不完美,这就是提问的原因。我强烈建议避免修复任何问题中的代码。 - harper
你好,通过 malloc*ptr = 12 的方式有什么区别?为什么前者即使在发送方本地声明,也会向调用者返回有效指针,而后者却不会呢? - SexyBeast
@AttitudeMonger 因为malloc表示“我需要一些内存来存储东西”,但是普通的*ptr = 12表示“我需要一些内存来进行计算,以后还可以用于其他事情”。 - wizzwizz4
相关内容:*从函数中返回指针* - Peter Mortensen
4个回答

47

在使用指针之前分配内存。如果没有分配内存,*point = 12会产生未定义的行为。

int *fun()
{
    int *point = malloc(sizeof *point); /* Mandatory. */
    *point=12;  
    return point;
}

此外,你的printf是错误的。你需要解引用(*)指针。

printf("%d", *ptr);
             ^

1
另外,int *point = calloc(num_values,sizeof(int)) 也会很有用。更多信息请参见http://www.thinkage.ca/english/gcos/expl/c/lib/calloc.html。 - tremendows
从函数中返回指针并让主程序释放内存是一个好的实践吗?如果不是,还有其他替代方法吗? - Mahesha Padyana
@cnicutar:如果你将malloc的结果转换为int*会更好,尽管在C中这并不是必需的。 - Destructor
tutorialpoint.com网站所述,从函数中返回指向局部变量的指针并不是个好主意。其中,他们举了一个例子,局部变量是数组(栈分配)。使用malloc/calloc分配内存并返回指针是否安全呢?顺便说一下,我从未遇到过这种做法的问题,也没有收到过gccvalgrind的警告或错误。 - Pantelis Sopasakis
@PantelisSopasakis 这取决于指针所指向的对象的存储类。如果对象具有“auto”存储并在超出范围时自动释放,则返回指针是不安全的。但是,如果您malloc了内存或者对象具有“static”存储,则指针是可以的。即使使用malloc分配内存仍然可能是道德上可疑的,因为这样您会强制调用者依赖于实现细节并且需要使用free释放它。 - cnicutar
显示剩余2条评论

20

尽管返回指向本地对象的指针是不良实践,但它并没有引起错误。以下是你出现段错误的原因:

int *fun()
{
    int *point;
    *point=12;  <<<<<<  your program crashed here.
    return point;
}
本地指针超出作用域,但真正的问题是对未初始化的指针进行解引用。指针变量point的值是多少?谁知道呢。如果该值没有映射到有效的内存位置,则会出现SEGFAULT错误。如果它偶然映射到某个有效位置,那么通过将该位置赋值为12来覆盖该位置,你刚刚破坏了内存。
由于返回的指针被立即使用,在这种情况下,可以返回本地指针。然而,这是一种不良的实践,因为如果该指针在另一个函数调用重新使用该栈内存后被重复使用,程序的行为将是未定义的。
int *fun()
{
    int point;
    point = 12;
    return (&point);
}

或者几乎相同:

int *fun()
{
    int point;
    int *point_ptr;
    point_ptr = &point;
    *point_ptr = 12;
    return (point_ptr);
}

另一个不好的做法但更安全的方法是将整数值声明为静态变量,这样它就不会在堆栈上,并且不会被另一个函数使用:

int *fun()
{
    static int point;
    int *point_ptr;
    point_ptr = &point;
    *point_ptr = 12;
    return (point_ptr);
}
或者
int *fun()
{
    static int point;
    point = 12;
    return (&point);
}

正如其他人所提到的,做这件事的“正确”方式是通过malloc在堆上分配内存。


1
在使用后,请释放它(使用 free() 函数)。 - C--
使用 static 会导致编译警告吗? - user12211554

0
它在将值12赋给整数指针时没有分配内存。因此它会崩溃,因为它找不到任何内存。
你可以尝试这个:
#include<stdio.h>
#include<stdlib.h>
int *fun();

int main()
{
    int *ptr;
    ptr=fun();
    printf("\n\t\t%d\n",*ptr);
}

int *fun()
{
    int ptr;
    ptr=12;
    return(&ptr);
}

12只存在于那个函数调用的堆栈上,这似乎会产生未定义的行为。 - scape

-1
据我所知,关键字new的使用与malloc(sizeof identifier)相对等效。下面的代码演示了如何使用关键字new。
    void main(void){
        int* test;
        test = tester();
        printf("%d",*test);
        system("pause");
    return;
}
    int* tester(void){
        int *retMe;
        retMe = new int;//<----Here retMe is getting malloc for integer type
        *retMe = 12;<---- Initializes retMe... Note * dereferences retMe 
    return retMe;
}

1
你从未开发过 C,但对于 C++ 不是很陌生吧? - Christian Gollhardt
这是正确的;然而,我发现在C语言中,new和delete确实可以用于内存管理。 - idleHands_94
2
这是完全不正确的。C语言没有关键字newdelete - hgiesel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接