C:返回本地结构有效但返回本地数组无效?

C: returning local struct works but returning local array does not?

据我所知,函数内的局部变量(未使用 static 关键字声明)仅在函数位于堆栈帧上且控制 return 到调用者所有局部变量都被销毁。这可以解释为什么不可能从函数 return 本地定义的数组。尝试取消引用这样的数组 returned 最好的情况下会给我们垃圾值,最坏的情况下会出现段错误。这不应该也适用于结构吗?也就是说,如果我们 return 从一个函数中构建一个结构,那么尝试访问它的成员不应该也会给我们垃圾值或段错误吗?

我有以下两个文件用于演示。第一个用于数组:

/* return_local_vs_malloc_array.c */
#include <stdio.h>
#include <stdlib.h>

#define LEN 10
#define VAL 100

int *return_local() {
    int arr[LEN] = {VAL};
    return arr;
}

int *return_mallocated() {
    int *arr = (int *)malloc(sizeof(int)*LEN);
    for (int i = 0; i < LEN; i++) 
        arr[i] = VAL;
    return arr;
}

void display(int *arr) {
    for (int i = 0; i < LEN; i++) {
        printf("%d ", arr[i]);
    }
    putchar('\n');
}

int main(void) {
    int *x = return_local();
    fputs("local: ", stdout); display(x);

    int *y = return_mallocated();
    fputs("malloc: ", stdout); display(y);
    free(y);

    return 0;
}

另一个用于结构:

/* return_local_vs_malloc_struct.c */
#include <stdio.h>
#include <stdlib.h>

struct foobar {
    int foo;
    int bar;
};

struct foobar return_local(int foo, int bar) {
    struct foobar f;
    f.foo = foo;
    f.bar = bar;
    return f;
}

struct foobar *return_mallocated(int foo, int bar) {
    struct foobar *f = (struct foobar *)malloc(sizeof(struct foobar));
    f->foo = foo;
    f->bar = bar;
    return f;
}

int main(void) {
    struct foobar x = return_local(10, 20);
    printf("local: x = {foo: %d, bar: %d}\n", x.foo, x.bar);

    struct foobar *y = return_mallocated(10, 20);
    printf("malloc: y = {foo: %d, bar: %d}\n", y->foo, y->bar);
    free(y);

    return 0;
}

当我 运行 我得到:

$ tcc -run ./return_local_vs_malloc_array.c
local: x = -1519296757 32767 0 0 -849137592 32767 -849137536 7 -849137608 32767
malloc: y = 100 100 100 100 100 100 100 100 100 100

~/
$ tcc -run ./return_local_vs_malloc_struct.c
local: x = {foo: 10, bar: 20}
malloc: y = {foo: 10, bar: 20}

如您所见,即使控制从声明它的函数中 returned,本地分配的结构仍然存在。据我所知,这是不应该发生的,并且使局部变量在被调用函数的生命周期之后持续存在的唯一方法是使用 static 限定符。但也许我错了?

当一个函数return是一个struct(或任何其他类型的变量)时,那么一个复制[=生成该变量或结构的 25=],并将该副本 return 发送给调用者。因此,即使函数结束时 'original' 已不复存在,副本仍然有效。

然而,当一个函数return是一个数组时,实际上是什么 returned 是该数组第一个元素的地址——见What is array to pointer decay? 因此,在这种情况下,当函数完成时,本地数组不再 'exists' 并且地址 returned 变得无效。

您可以 'emulate' 通过将函数重新定义为 return 局部变量的 地址 来 'emulate' struct 的这种行为,就像这样:

struct foobar {
    int foo;
    int bar;
};

// Note: BAD function, for example purpose only. DO NOT USE IT!
struct foobar* return_local(int foo, int bar) // Return a pointer-to-struct
{
    struct foobar f;
    f.foo = foo;
    f.bar = bar;
    return &f; // Any caller that attempts to use this returned pointer will
               // exhibit Undefined Behaviour, as the 'f' struct will be gone!
}

您不能 return 数组,因为数组不能用作其他数组的初始值设定项。那就是你不能写例如

int arr1[LEN] = {VAL};
int arr2 = arr1;

因此,当您将数组指示符用作 return 语句的表达式时,它会隐式转换为指向其第一个元素的指针。也就是说,returned 的是指针,而不是数组本身。并且指针失效是因为指针指向的自动存储时长的数组在函数退出后不会存活

另一方面,一个结构类型的对象可以用作另一个结构类型对象的初始值设定项。例如

struct foobar f1 = { .foo = 10, .bar = 20 };
struct forbar f2 = f1;

因此,当结构类型的对象从函数中 returned 时,会创建 returned 对象的副本,该副本可以分配给结构类型的对象函数的调用者。