C语言中传递外部指针时getline()输出null的问题排查
文件读取重构后的问题排查与修复
问题现象
原本在batch_mode函数内直接调用getline读取文件行功能正常,将读取逻辑抽离为独立的read_line函数后出现两个问题:
read_line内部通过printf能正常输出读取到的内容,但回到batch_mode函数后,line变量始终为NULL- 将
batch_mode改为循环调用read_line的形式后,出现无法读取文件的报错,提示文件被多次执行
相关代码
main函数
int main(int argc, char *argv[]) { if (argc > 2) return EXIT_FAILURE; batch_mode(argv[1]); return EXIT_SUCCESS; }
初始版本batch_mode函数
void batch_mode(char *path) { char *line = NULL; ssize_t error = 0; char *cmd = NULL; FILE *file = fopen(path, "r"); if (file == NULL) { printf("Coudn't read file\n"); return; } while (error != -1) { read_line(line, file, cmd); error = -1; int thread = fork(); if (thread == 0) { char *myargs[] = {NULL}; execv(cmd, myargs); } else { thread = wait(NULL); } } if (file != NULL) fclose(file); free(line); }
存在问题的read_line函数
ssize_t read_line(char *line, FILE *file, char *cmd) { size_t len = 0; ssize_t read = 0; read = getline(&line, &len, file); printf("%s\n", line); if (read == -1) { printf("Coudn't read line\n"); return -1; } char *copy = line; cmd = strsep(©, " "); printf("%s\n", cmd); return 0; }
修改循环逻辑后的batch_mode函数
void batch_mode(char *path) { char *line = NULL; ssize_t error = 0; char *cmd = NULL; FILE *file = fopen(path, "r"); if (file == NULL) { printf("Coudn't read file\n"); return; } while ((error = read_line(line, file, cmd)) != -1) { int thread = fork(); if (thread == 0) { char *myargs[] = {NULL}; execv(cmd, myargs); } else { thread = wait(NULL); } } if (file != NULL) fclose(file); free(line); }
问题根源
- 指针传递错误:C语言采用值传递,
read_line函数中的line和cmd是外部变量的局部副本。getline(&line, ...)修改的是副本指针的指向,不会影响batch_mode中的原变量;cmd = strsep(...)同样只是给局部cmd赋值,外部的cmd变量完全没接收到结果。 - 循环逻辑错误:初始版本的
batch_mode循环仅执行一次read_line就将error设为-1,根本没有实现循环读取;修改后的循环虽然结构正确,但因指针传递问题无法正确读取行内容,加上execv参数错误导致子进程执行异常,间接引发文件读取相关报错。
修复方案
1. 修改read_line函数,传递指针的指针
通过传递指针的地址(char **line、char **cmd),让函数内部的修改能同步到外部变量:
ssize_t read_line(char **line, FILE *file, char **cmd) { size_t len = 0; ssize_t read = getline(line, &len, file); if (read == -1) { printf("无法读取行\n"); return -1; } printf("%s\n", *line); char *copy = *line; *cmd = strsep(©, " "); printf("%s\n", *cmd); return 0; }
2. 修正batch_mode函数的调用与逻辑
调整read_line的调用方式,传递指针的地址,同时修复execv的参数错误:
void batch_mode(char *path) { char *line = NULL; ssize_t error = 0; char *cmd = NULL; FILE *file = fopen(path, "r"); if (file == NULL) { printf("无法读取文件\n"); return; } while ((error = read_line(&line, file, &cmd)) != -1) { int pid = fork(); if (pid == 0) { // execv要求参数数组首元素为命令本身 char *myargs[] = {cmd, NULL}; execv(cmd, myargs); // 若execv执行失败,必须退出子进程 perror("execv执行失败"); exit(EXIT_FAILURE); } else if (pid > 0) { wait(NULL); } else { perror("fork失败"); } } if (file != NULL) fclose(file); free(line); }
额外说明
execv的参数数组第一个元素必须是命令路径/名称,否则会执行失败- 子进程中若
execv调用失败,必须调用exit退出,避免子进程继续执行父进程的后续逻辑 getline分配的内存由调用者(batch_mode中的line)负责释放,避免内存泄漏
内容的提问来源于stack exchange,提问作者droppre
相关产品推荐
相关产品推荐

