未分配内存的char*调用strncpy为何在Socket服务器程序中正常运行,测试程序却出现段错误?
嗨,这问题其实核心点在于:你的服务器程序根本不是“正常运行”,只是刚好撞上了“未定义行为”里的幸运情况而已——C语言里未初始化的指针行为完全不可预测,能跑是运气,崩了才是常态。
先搞懂:未初始化指针到底是什么?
当你写char *line;却不赋值的时候,这个指针里存的是随机的垃圾值,它指向内存里的某个随机地址——可能是一块当前程序有权限读写的空闲内存,也可能是只读的代码段、内核空间,或者已经被其他变量占用的关键区域。
为什么服务器程序没崩?
你的parse_http_req函数里,未初始化的line指针刚好指向了一块程序当前可以安全写入的内存(比如栈上还没被用到的空间,或者堆里残留的空闲区域),所以strncpy把数据写进去的时候没触发错误,甚至后续的line[size] = '\0';也没破坏到关键数据。但这纯粹是巧合,你换个编译器编译、换个操作系统跑,甚至多请求几次,说不定哪天就突然崩了,或者出现莫名其妙的bug(比如悄悄改了其他变量的值)。
为什么测试程序直接崩了?
而test.c的f1函数里,未初始化的l1指针刚好指向了一块程序没有写入权限的内存区域(比如只读的字符串常量区,或者已经被释放的内存),strncpy尝试写入的时候就触发了段错误——这才是未定义行为更常见的表现,刚好帮你提前发现了问题。
正确的写法应该怎么做?
你需要给line分配合法的内存空间,常见的两种方式:
- 用堆内存分配:
char *line = malloc(size + 1); // +1是给终止符'\0'留位置 if (line == NULL) { // 处理内存分配失败的情况 perror("malloc failed"); return; } strncpy(line, req, size); line[size] = '\0'; // 用完记得释放内存 free(line); - 用栈数组(适合size不大的情况):
如果size不会超过栈的容量(比如你的MAX_REQ_SIZE是1024,栈一般足够),可以直接在栈上声明数组:char line[size + 1]; strncpy(line, req, size); line[size] = '\0'; - 更高效的方式:直接在原buffer上修改
既然你已经找到了第一行的结尾('\n'的位置),其实不需要复制到新变量,直接把req[size]改成'\0'就行,这样省掉了内存分配的步骤:req[size] = '\0'; printf("First line: %s\n", req); // 如果之后还要用原buffer,记得改回去,或者先保存原来的字符
顺便提个小bug
你的get_req_method函数里有个明显的逻辑错误——strcmp返回0的时候才表示两个字符串相等,你现在的条件写反了!比如传入"GET"的话,strcmp(req, "GET")返回0,条件if (strcmp(req, "GET"))不成立,会跳过这个分支,最后返回INVALID_METHOD,这完全不是你想要的效果。正确的写法应该是:
if (strcmp(req, "GET") == 0) { return GET; }
其他方法的判断也要改成这样哦。
备注:内容来源于stack exchange,提问作者Eivor

