我是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结束时?怎么可能?
答案 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];
是堆栈内存。
您分配并释放自己的内存,通常使用malloc
或calloc
或realloc
或其他一些功能,例如strdup
。一旦分配,堆内存将保持不变,直到它被明确解除分配。
经验法则:堆内存可以从函数返回,堆栈内存不能......好吧,你可以但是那个内存插槽可能会被其他东西使用。这就是发生在你身上的事情。
所以你需要分配内存,不仅仅是一次,而是多次。
string_t
结构的指针数组分配内存。string_t
结构分配内存。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语言进行编码,那就是为计算机提供高效服务。
答案 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()
的现有数组的元素。该阵列基本上适用于该计划的整个生命周期。