返回C阵列时财富?

时间:2016-01-04 18:31:48

标签: 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结束时?怎么可能?

3 个答案:

答案 0 :(得分:3)

这段代码永远不会完美,它恰好正常工作。在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字符串(实际上是一个数组)分配内存。
  4. 然后你必须释放所有这些。听起来像很多工作?它是!欢迎来到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语言进行编码,那就是为计算机提供高效服务。

答案 1 :(得分:2)

因为函数中创建了v1中test_data的空间,并且当函数返回时该空间被回收(因此可以用于其他事情);在v2中,空间被放置在函数之外,因此不会被回收。

答案 2 :(得分:1)

  

为什么函数generate_test_data_v1仅在“test_data_size = 10”时有效,但当“test_data_size = 20”进程以退出代码11结束时?

我认为函数generate_test_data_v1()没有理由失败,但你不能使用它的返回值。它返回一个指向属于函数范围的自动变量的指针,当它们所属的函数返回时,自动变量不再存在。您的程序在取消引用该指针时会产生未定义的行为。我可以相信它看起来像你想要的一些尺寸,但即使在那些情况下程序也是错误的。

此外,您的程序不太可能产生11的退出代码,但它可能会突然终止并出现分段错误,即信号11。

  

为什么generate_test_data_v2完美无缺?

函数generate_test_data_v2()填充属于函数main()的现有数组的元素。该阵列基本上适用于该计划的整个生命周期。