堆/栈和函数返回值

4

如果这是一个“基础”问题,我很抱歉,因为我刚接触C语言,找不到答案。我的问题是关于在函数内部需要使用malloc来分配变量内存并返回其指针,与在函数内创建变量并通过return返回结果之间的区别。

首先要考虑的问题是,在函数内声明的任何变量都会在函数终止时被销毁;那么为什么下面的代码是有效的:

int add(int a, int b)
{
    int result;
    result = a + b;
    return result;
}

但以下内容不是吗?
char *concat(char* a, char* b)
{
    char result[10];
    strcat(result, a);
    strcat(result, b);
    return result;
}

你得到的警告是你正在返回一个局部变量的地址,但这也是我们在第一个函数中所做的?行为是否因类型而异?

举个更真实的例子,我非常困惑应该使用以下两个函数中的哪一个,因为它们都很适合我的程序:

struct Card *card_create(enum Rank rank, enum Suit suit)
{
    struct Card *card = malloc(sizeof(struct Card));
    if(card == NULL) {
        fprintf(stderr, "malloc: %s", strerror(errno));
        return NULL;
    }
    card->rank = rank;
    card->suit = suit;
    return card;
}

或者:

struct Card card_create(enum Rank rank, enum Suit suit)
{
    struct Card card;
    card.rank = rank;
    card.suit = suit;
    return card;
}

再次抱歉,如果这是一个新手问题,但我真的很希望能得到解释。谢谢!


你得到的警告是你正在返回一个局部变量的地址,但这也是我们在第一个函数中所做的吗?不是的。第一个函数返回一个局部变量的值,而第二个函数返回一个局部变量的地址 - 这是两种不同的情况,第一个有意义,而第二个没有。 - Wojtek Surowka
这无疑是一个重复问题 - 问题在于它是哪个问题的好重复。 - Jonathan Leffler
请参考这个链接(或者搜索“返回局部变量地址”等相关内容),以及这个链接,了解如何通过值返回结构体。需要注意的是,在返回语句中,数组会退化为指针,但结构体不会。此外,需要明确的是,一个对象的地址是一个值,但一个值不一定是一个地址(也永远不是一个对象,但一个对象可能有一个值)。 - mafso
4个回答

8
在您的add()函数中,您返回保存在其局部变量result中(直到函数退出)的。该变量的存储空间在函数返回后不再可用,但存储在其中的值只是一个数字,本身并不依赖于该存储。
在您的concat()函数中,表达式result评估为指向包含10个char数组的局部存储的指针。您仍然可以返回指针的值,但一旦函数退出,该值的含义就不再定义。
因此,否,返回值本身在这些情况下并没有区别,但与此相关的有用性 - 甚至风险 - 却大相径庭。

4

行为是否因类型而异?

有点不同。在C中返回值时,返回的是值,而不是指向该值或依赖于任何仍存在的变量的指针。但是,还有一个无关的规则,几乎在所有上下文中,数组类型的值都会被隐式转换为指向数组第一个元素的指针。因此,第二个代码片段返回的是数组中的指针,而第一个代码片段只返回一个int。


非常感谢您的回答。那么,如果要返回的值是指针/数组,函数只需要返回一个指针,这样说是否正确?因此,在这两个结构体的示例中,第一个代码片段(不使用指针)会更好吗? - Cristian
@Cristian:我可能会使用返回struct Card而不是struct Card *的那个。(你可能把代码片段的顺序搞反了。) - user2357112

2

非常有趣的问题

当一个函数返回局部变量作为值时,将创建一个新的变量并存储在CPU寄存器中(取决于编译器)。之后,局部变量将被释放。

 int add(int a, int b)
 {
     int result;
     result = a + b;

     return result; // 1. A new int variable will be created and stored in CPU registers (depends on Compiler)

     // 2. result will be freed at the end of function
 }

在CPP中,与C语言非常相似,卡片的构造函数将被调用两次。

 struct Card card_create(enum Rank rank, enum Suit suit)
 {
     struct Card card;
     card.rank = rank;
     card.suit = suit;

     return card; // 1. New Card object will be created and stored in CPU register(Depends on compiler)

     // 2. card will be freed at the end of function
 }

希望这可以帮到您。

1
在第一个代码片段中,返回了一个int变量的值,非常好。
在第二个代码片段中,返回了一个本地(堆栈)变量的地址,不好。
"并且,也许这一行:

"
char[10] result;

would be better as:

char result[10];

如果第二个示例声明结果如下:
char result;

那么 result 中的值可以被返回:

return(result);

然而,第二个例子将result定义为char类型的数组,使得result成为指向该数组开头的指针。因此,第二个例子中result的真实值是指向本地堆栈内存的地址(当函数作用域终止时会消失)。
第一个例子捕获了result的值(一个整数值),并将该值发送回调用者(而不是函数局部作用域中的值的地址)。

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