c - 返回 C 数组时的财富?

标签 c arrays function

我是 C 的新手,在使用以下 C 函数时被一些魔法惊呆了。 此代码适用于我,并打印所有数据。

typedef struct string_t {
    char *data;
    size_t len;
} string_t;


string_t *generate_test_data(size_t size) {
    string_t test_data[size];
    for(size_t i = 0; i < size; ++i) {
        string_t string;
        string.data = "test";
        string.len = 4;
        test_data[i] = string;
    }
    return test_data;
}

int main() {
    size_t test_data_size = 10;
    string_t *test_data = generate_test_data(test_data_size);
    for(size_t i = 0; i < test_data_size; ++i) {
        printf("%zu: %s\n", test_data[i].len, test_data[i].data);
    }
}

为什么函数 generate_test_data 仅在“test_data_size = 10”时有效,但在“test_data_size = 20”过程结束时退出代码为 11?怎么可能?

最佳答案

这段代码永远不会完美运行,它只是碰巧可以运行。在 C 中,您必须自己管理内存。如果您犯了一个错误,该程序可能会继续运行……或者某些东西可能会在您认为属于您的内存中乱涂乱画。这通常表现为您所遇到的奇怪错误:它在长度为 X 时有效,但在长度为 Y 时失败。

如果您打开 -Wall,或者如果您使用 clang 甚至更好 -Weverything,您将收到这样的警告。

test.c:18:12: warning: address of stack memory associated with local variable 'test_data' returned
      [-Wreturn-stack-address]
    return test_data;
           ^~~~~~~~~

C 中两种重要的内存类型是:栈和堆。基本上,堆栈内存仅在函数运行期间有用。当函数返回时,堆栈上声明的任何内容都会自动释放,有点像其他语言中的局部变量。经验法则是,如果您没有显式分配它,它就在堆栈上。 string_t test_data[size];为栈内存。

您自己分配和释放的堆内存,通常使用 malloccallocrealloc 或其他为您执行此操作的函数,例如 strdup。分配后,堆内存会一直存在,直到它被显式释放。

经验法则:堆内存可以从函数返回,栈内存不能……好吧,你可以但是那个内存槽可能会被其他东西使用。这就是发生在你身上的事情。

所以你需要分配内存,不只是一次,而是多次。

  1. 为指向 string_t 结构的指针数组分配内存。
  2. 为数组中的每个 string_t 结构分配内存。
  3. 为每个结构中的每个 char 字符串(实际上是一个数组)分配内存。

然后你必须释放所有这些。听起来工作量很大?这是!欢迎来到 C。抱歉。您可能想编写函数来分配和释放 string_t

static string_t *string_t_new(size_t size) {
    string_t *string = malloc(sizeof(string_t));
    string->len = 0;

    return string;
}

static void string_t_destroy(string_t *self) {
    free(self);
}

现在您的测试数据函数如下所示。

static string_t **generate_test_data_v3(size_t size) {
    /* Allocate memory for the array of pointers */
    string_t **test_data = calloc(size, sizeof(string_t*));

    for(size_t i = 0; i < size; ++i) {
        /* Allocate for the string */
        string_t *string = string_t_new(5);

        string->data = "test";
        string->len = 4;
        test_data[i] = string;
    }

    /* Return a pointer to the array, which is also a pointer */
    return test_data;
}

int main() {
    size_t test_data_size = 20;

    string_t **test_data = generate_test_data_v3(test_data_size);

    for(size_t i = 0; i < test_data_size; ++i) {
        printf("%zu: %s\n", test_data[i]->len, test_data[i]->data);
    }

    /* Free each string_t in the array */
    for(size_t i = 0; i < test_data_size; i++) {
        string_t_destroy(test_data[i]);
    }

    /* Free the array */
    free(test_data);
}

可以不使用指针而是复制您使用的所有内存,这与您之前所做的类似。这对程序员来说更容易,但对计算机来说效率低下。如果您使用 C 语言编写代码,那么一切都是为了提高计算机的效率。

关于c - 返回 C 数组时的财富?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34597526/

相关文章:

mysql - 聚合结果的 SQL 查询

java - 什么都不接受也不返回什么的功能接口(interface)

java - 显示一个 ArrayList?

c - 使用两个不同的函数来更改 main 和 print 中的变量

C unsigned char 溢出时的意外行为

c - 如何通过 RAM 上的索引设置变量值?

java - 为什么我的程序不输出数组元素?

arrays - Fortran 用户定义运算符中未分配的假定形状数组的问题

c++ - 模板类中 float 和 double 文字的函数特化

c++ - 编译时 -pthread 标志的意义