C语言字符串拼接出现意外结果的原因排查
问题分析与解决:C语言路径拼接重复问题
问题现象
这段C程序意图为argv传入的路径末尾补加/(若路径未以/结尾),再拼接文件名,但出现以下异常:
- 循环首次迭代输出正常路径,第二次迭代会出现重复拼接,例如输出
/home/developer/folder/anotherfolder/test.shtest.sh - 线程版本运行到第三次迭代会因内存/资源不足报错
问题代码
#include <pthread.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> void *full_path(); typedef struct th_ar { int argc; char **argv; }; int main(int argc, char **argv) { // pthread_t thread_id; while (1) { sleep(5); struct th_ar ta = {.argc = argc, .argv = argv}; // pthread_create(&thread_id, NULL, full_path, (void *)&ta); full_path((void *)&ta); } return 0; } void *full_path(void *args) { struct th_ar *ta = (struct th_ar *)args; char *path = ta->argv[1]; printf("path: %s\n", path); char *filename = "test.sh"; char *full_path = (char *)malloc(strlen(path) + strlen(filename) + 2); // 此处计算的长度是正确的 // char *full_path = (char *)malloc(strlen(path) + strlen(filename) + 4096); if (full_path == NULL) return (void *)1; strcpy(full_path, path); char last_char = path[strlen(path) - 1]; if (last_char == '/') { // printf("%s\n", "It ends in /"); } else { // printf("%s\n", "It does not end in /"); // printf("%s\n", "Appending / at the end"); full_path[strlen(full_path)] = '/'; } strcat(full_path, filename); printf("full_path: %s\n", full_path); free(full_path); return (void *)0; }
错误根源
核心问题是手动添加/后未补全字符串结束符\0:
- 当路径不以
/结尾时,执行full_path[strlen(full_path)] = '/',这一步将/覆盖了原字符串的结束符\0,导致full_path变成了没有终止符的“无效字符串”。 - 后续调用
strcat(full_path, filename)时,strcat会从full_path起始位置一直找\0,找不到就会越界访问内存。 - 第一次运行时,
malloc分配的内存后恰好有\0,结果看似正常;第二次运行时,malloc复用了之前释放的内存,这块内存残留了上一次的test.sh内容,strcat就会从错误位置拼接,导致重复。 - 线程版本的资源不足问题,是因为创建线程后未调用
pthread_join等待线程结束,线程资源无法回收,多次创建后耗尽系统线程资源。
解决方法
1. 修复字符串结束符问题
两种可选方案,确保字符串始终有\0:
- 方案一:手动添加
/后补终止符else { int len = strlen(full_path); full_path[len] = '/'; full_path[len + 1] = '\0'; // 必须补终止符 } - 方案二:用
strcat添加/,自动处理终止符else { strcat(full_path, "/"); }
2. 修复线程版本资源泄漏
每次创建线程后调用pthread_join等待线程结束,回收资源:
int main(int argc, char **argv) { pthread_t thread_id; while (1) { sleep(5); struct th_ar ta = {.argc = argc, .argv = argv}; pthread_create(&thread_id, NULL, full_path, (void *)&ta); pthread_join(thread_id, NULL); // 等待线程结束,回收资源 } return 0; }
额外优化建议
- 减少重复调用
strlen(path):提前计算一次路径长度,提升效率size_t path_len = strlen(path); char *full_path = (char *)malloc(path_len + strlen(filename) + 2); char last_char = path[path_len - 1]; - 增加空路径判断:避免
path[strlen(path)-1]访问越界size_t path_len = strlen(path); if (path_len == 0) { strcpy(full_path, "/"); } else { // 原逻辑 }
内容的提问来源于stack exchange,提问作者Eduard
相关产品推荐
相关产品推荐

