C语言使用scanf()读取未知长度字符串的初始化方法
问题根因
你写的代码触发段错误的核心逻辑很简单:char *str = NULL 只是声明了一个字符指针,根本没有分配可写入的合法内存,scanf 拿到这个NULL指针会直接往非法地址写数据,必然触发内存访问错误。
另外要明确:C标准里的%s格式符不会自动帮你分配内存,你必须传入一块提前准备好的、足够容纳输入内容的可写内存,它才会正常工作。
可行方案
以下两种方案都满足「不需要预先知道输入字符串长度、不需要硬编码超大定长数组」的要求:
方案1:POSIX系统下用%ms修饰符(代码最简洁)
Linux、macOS等遵循POSIX标准的系统中,scanf的%s支持m修饰符,会自动根据实际输入的字符串长度申请对应大小的内存,完全不需要你提前预估长度:
#include <stdio.h> #include <stdlib.h> int main(void) { char *str = NULL; int num; // 注意这里是%ms,传入str的地址,scanf会自己修改str指向新分配的内存 int read_res = scanf("%ms %d", &str, &num); if (read_res != 2) { printf("输入格式不符合要求\n"); return 1; } printf("str: %s\nnum: %d\n", str, num); free(str); // 必须手动释放scanf申请的内存,避免泄漏 return 0; }
这个方案代码量最小,但注意它不属于C语言标准特性,Windows下的MSVC编译器默认不支持,需要跨全平台兼容就用下面的方案。
方案2:逐字符读取+动态扩容(全编译器通用)
如果需要兼容不支持%ms的环境,可以自己实现动态扩容逻辑:逐字符读取输入,当前申请的内存装不下了就扩容一块,直到读到字符串和数字的分隔符为止,不需要提前知道输入长度:
#include <stdio.h> #include <stdlib.h> int main(void) { char *str = NULL; int num; int cap = 0; // 已申请的内存总大小 int str_len = 0; // 已读取的字符串长度 char cur; // 先跳过字符串前的空白符(空格、换行、制表符) while ((cur = getchar()) != EOF && (cur == ' ' || cur == '\n' || cur == '\t')); if (cur == EOF) { printf("输入异常\n"); return 1; } // 逐字符读字符串,碰到空白分隔符就停 do { // 剩余空间不够存新字符+末尾的'\0'时就扩容,每次扩16字节即可,可按需调整步长 if (str_len + 1 >= cap) { cap += 16; char *tmp = realloc(str, cap); if (!tmp) { free(str); printf("内存分配失败\n"); return 1; } str = tmp; } str[str_len++] = cur; cur = getchar(); } while (cur != EOF && cur != ' ' && cur != '\n' && cur != '\t'); str[str_len] = '\0'; // C字符串必须补结束符,不然printf会读越界 // 读完字符串剩下的部分直接读整数即可 if (scanf("%d", &num) != 1) { free(str); printf("数字格式错误\n"); return 1; } printf("str: %s\nnum: %d\n", str, num); free(str); return 0; }
注意:所有通过
malloc/realloc/%ms申请的堆内存,用完都必须手动调用free()释放,否则会出现内存泄漏。
内容的提问来源于stack exchange,提问作者mariekart42
相关产品推荐
相关产品推荐

