C语言入门疑问:结构体中字符串为何无需调用malloc?
我这周刚学C语言,遇到一个想不通的矛盾点:
- 我知道函数结束后,栈上的内容会被释放,要让变量在作用域结束后还能用得用
malloc。结构体也是如此,如果不用malloc分配结构体,在创建它的函数外打印值会报错。 - 但我发现,如果用
malloc分配结构体,然后直接把char指针(字符串)赋值给结构体成员,程序能正常打印——我原本以为得先给结构体成员malloc,再拷贝字符串进去才行?
下面这段代码能正常打印结构体成员:
typedef struct person { char *name; } person; // print struct fields void print_person(person *input) { printf("Name: %s", input->name); } person *person_generator(char *name) { person *person = malloc(sizeof(person)); // assign given char pointer to struct field person->name = name; return person; } person *create_default_person() { person *new_person = person_generator("Alfred Hitchcock"); return new_person; } int main() { person *default_person = create_default_person(); print_person(default_person); return 0; }
我困惑的是,为什么下面这段需要malloc成员并拷贝的代码不是必须的?
typedef struct person { char *name; } person; // print struct fields void print_person(person *input) { printf("Name: %s", input->name); } person *person_generator(char *name) { person *person = malloc(sizeof(person)); // didnt need to do this? person->name = malloc(strlen(name) + 1); strcpy(person->name, name); return person; } char *get_string() { char *item = "Alfred Hitchcock"; return item; } person *create_default_person() { char *value = get_string(); person *new_person = person_generator(value); return new_person; } int main() { person *default_person = create_default_person(); print_person(default_person); return 0; }
我本来以为create_default_person()结束后,内部的指针会被销毁,结构体成员应该指向垃圾数据才对?
补充问题:
有人说字符串字面量会在程序整个生命周期中存储,那再看这个从文件读取字符串的例子,不调用malloc直接赋值给结构体成员,为什么也能正确打印?
typedef struct person { char *name; } person; void print_person(person *input) { printf("Name: %s", input->name); } person *person_generator(char *name) { person *person = malloc(sizeof(person)); person->name = name; return person; } person *create_default_person() { FILE *fp; fp = fopen("./data.txt", "r"); char buffer[255]; if (fp == NULL) { printf("Couldnt open\n"); return NULL; } fgets(buffer, 255, fp); fclose(fp); person *new_person = person_generator(buffer); return new_person; } int main() { person *human = create_default_person(); if (human == NULL) { printf("Failed to find human"); return 0; } print_person(human); return 0; }
核心原因:字符串的存储位置决定了指针的有效性
1. 字符串字面量的特殊存储
你第一个例子里的"Alfred Hitchcock"是字符串字面量,它既不在栈上,也不在堆上,而是存在程序的静态存储区。这块内存会在程序启动时分配,直到程序结束才会释放。
所以即使create_default_person()函数执行完毕,结构体成员name指向的依然是静态存储区里的字符串字面量,内存没有被回收,自然能正常打印。
这种情况下,确实不需要给结构体的name成员malloc——因为你只是复用了静态存储区里已经存在的字符串,没有必要再拷贝一份。
2. 文件读取例子:只是“侥幸”能运行,属于未定义行为
你补充的文件读取例子里,buffer是create_default_person()函数里的栈局部数组。函数结束后,栈帧被回收,buffer占用的内存会被标记为可用,后续其他函数调用可能会覆盖这块内存。
你现在能正常打印,只是因为函数刚结束,这块内存还没被改写而已——这是完全不可靠的,属于C语言里的未定义行为。如果在print_person()之前调用其他函数,或者编译器优化等级不同,大概率会输出垃圾数据甚至程序崩溃。
这种场景下,才必须给结构体的name成员malloc,把buffer里的字符串拷贝到堆内存中,这样即使栈上的buffer被回收,堆上的字符串依然有效。
3. 什么时候需要给结构体的字符串成员malloc?
总结两种必须malloc的场景:
- 当你要保存的字符串是栈上的局部变量(比如函数里的char数组、栈上的临时字符串);
- 当你需要字符串的生命周期独立于原数据(比如原字符串可能被修改、释放,而结构体需要保留自己的副本)。
如果字符串本身存在静态存储区(比如字符串字面量),或者已经在堆上(比如别人malloc好传给你的),那直接赋值指针就行,不需要额外malloc。
内容的提问来源于stack exchange,提问作者Nathaniel Woodbury

