从函数返回char数组至main后,printf输出乱码问题排查
Hey, I see exactly what's going on here—you're hitting a classic C memory management pitfall with local stack variables. Let me break this down for you:
问题根源
你在run_cmd()里定义的output[2000]是栈上的局部变量。在C语言中,局部变量的内存会在函数执行完毕返回时被销毁(栈帧被清理)。虽然你返回了指向这个数组的指针,但等到main()尝试打印它时,这块栈内存已经被后续代码(比如main里的printf调用)覆盖了。这就是为什么你看到乱码——指针地址看起来正确,但指向的内存已经无效了。
修复方案
根据你的需求,这里有三种可靠的解决方式:
1. 使用静态数组
把output声明为static变量,这样它会从栈转移到全局数据段,函数返回后不会被销毁。注意:静态变量是全局共享的,如果多次调用run_cmd(),之前的结果会被覆盖。
修改后的run_cmd()片段:
char * run_cmd(char * cmd ) { static char output[2000]; // 添加static关键字 // 其余代码基本不变... // 记得把Sleep替换成下文提到的进程等待逻辑! }
2. 使用动态内存分配
用malloc()在堆上分配内存,这块内存会一直存在直到你显式调用free()释放。需要注意在main()中释放内存,避免内存泄漏。
完整修改代码:
#include <Windows.h> #include <stdio.h> #include <stdlib.h> // 引入malloc/free的头文件 char * run_cmd(char * cmd ) { char *output = malloc(2000); // 在堆上分配内存 if (output == NULL) { // 务必检查malloc是否成功 fprintf(stderr, "内存分配失败\n"); return NULL; } SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; HANDLE hFile; hFile = CreateFileA("out.log", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); PROCESS_INFORMATION pi; STARTUPINFO si; BOOL ret = FALSE; DWORD flags = CREATE_NO_WINDOW; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags |= STARTF_USESTDHANDLES; si.hStdInput = NULL; si.hStdError = NULL; si.hStdOutput = hFile; ret = CreateProcessA(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi); // 替换Sleep(2000)为可靠的进程等待 if (ret) { WaitForSingleObject(pi.hProcess, INFINITE); // 等待进程执行完毕 CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } CloseHandle(hFile); DWORD lpNumberOfBytesRead; hFile = CreateFileA("out.log", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); DWORD dwBytesToRead = GetFileSize(hFile, NULL); // 防止缓冲区溢出 if (dwBytesToRead > 1999) { dwBytesToRead = 1999; } ReadFile(hFile, (void *)output, dwBytesToRead, &lpNumberOfBytesRead, NULL); output[lpNumberOfBytesRead] = '\0'; CloseHandle(hFile); printf("%s\n", output); printf("------------------------------------------------------------------------"); return output; } int main(void) { char *result = run_cmd("ipconfig"); if (result != NULL) { printf("%s\n", result); free(result); // 记得释放堆内存! } return 0; }
3. 让调用者提供缓冲区
不在run_cmd()内分配内存,而是让main()传入一个缓冲区和它的大小。这样调用者拥有内存的所有权,完全避免内存管理问题。
完整修改代码:
#include <Windows.h> #include <stdio.h> // 返回写入缓冲区的字节数,出错返回-1 int run_cmd(char * cmd, char *output, size_t output_size) { SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; HANDLE hFile; hFile = CreateFileA("out.log", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); PROCESS_INFORMATION pi; STARTUPINFO si; BOOL ret = FALSE; DWORD flags = CREATE_NO_WINDOW; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags |= STARTF_USESTDHANDLES; si.hStdInput = NULL; si.hStdError = NULL; si.hStdOutput = hFile; ret = CreateProcessA(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi); if (ret) { WaitForSingleObject(pi.hProcess, INFINITE); // 等待进程执行完毕 CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } CloseHandle(hFile); DWORD lpNumberOfBytesRead; hFile = CreateFileA("out.log", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); DWORD dwBytesToRead = GetFileSize(hFile, NULL); // 预留空间给字符串结束符 if (dwBytesToRead >= output_size) { dwBytesToRead = output_size - 1; } ReadFile(hFile, (void *)output, dwBytesToRead, &lpNumberOfBytesRead, NULL); output[lpNumberOfBytesRead] = '\0'; CloseHandle(hFile); printf("%s\n", output); printf("------------------------------------------------------------------------"); return lpNumberOfBytesRead; } int main(void) { char output[2000]; run_cmd("ipconfig", output, sizeof(output)); printf("%s\n", output); return 0; }
额外修复:替换Sleep为可靠的进程等待
你当前用的Sleep(2000)是个临时方案——无法保证进程会在2秒内执行完毕。改用WaitForSingleObject(pi.hProcess, INFINITE)可以确保等到命令完全执行结束后再读取输出文件,这样每次都能获取完整的结果。
内容的提问来源于stack exchange,提问作者user1720897

