C语言for循环初始化charInfo数组致程序静默终止问题排查
循环初始化CHAR_INFO数组时程序无报错意外终止
在尝试通过任意迭代次数的for循环(或任意类型循环)初始化charInfo[]数组时,程序会无任何错误提示意外终止,相关实现代码如下:
#include <stdlib.h> #include <windows.h> int main(){ // 创建屏幕缓冲区句柄、用于访问控制台光标信息的指针 HANDLE handleNewScreenBuffer; CONSOLE_CURSOR_INFO *ConsoleCursorInfo; // 创建要绘制到屏幕的矩形区域、字符信息数组 SMALL_RECT rectangle = {0, 0, 64, 7}; CHAR_INFO charInfo[520]; COORD coordBufSize = {65, 8}; COORD coordBufCoord = {0, 0}; handleStandardOut = GetStdHandle(STD_OUT_HANDLE); handleNewScreenBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); GetConsoleCursorInfo(handleNewScreenBuffer, ConsoleCursorInfo); ConsoleCursorInfo->bVisible = 0; SetConsoleCursorInfo(handleNewScreenBuffer, ConsoleCursorInfo); // 向字符信息数组写入绿色背景、红色的&字符 for (int x = 0; x < (sizeof(charInfo)/4); ++x){ charInfo[x].Attributes = BACKGROUND_GREEN | FOREGROUND_RED; charInfo[x].Char.AsciiChar = (char)'&'; } WriteConsoleOutput(handleNewScreenBuffer, charInfo, coordBufSize, coordBufCoord, &rectangle); SetConsoleActiveScreenBuffer(handleNewScreenBuffer); Sleep(5000); return 0; }
补充说明:手动为数组元素赋值时无任何问题,程序可按预期运行,例如下方仅手动初始化控制台窗口对应首尾数组元素的代码可正常执行:
charInfo[0].Attributes = BACKGROUND_GREEN | FOREGROUND_RED; charInfo[0].Char.AsciiChar = (char)'&'; charInfo[519].Attributes = BACKGROUND_GREEN | FOREGROUND_RED; charInfo[519].Char.AsciiChar = (char)'&';
注:我知晓该实现方式已过时,仅做技术探索使用。
我曾怀疑进入循环时数组会离开作用域,但该逻辑并不成立,暂未找到其他可能的故障原因。
问题更新
经进一步排查,删除代码中handleStandardOut = GetStdHandle(STD_OUT_HANDLE);语句后问题即可解决。但目前仍不明确为何创建标准输出缓冲区句柄(当前场景暂未使用该句柄)会引发此类异常行为。
问题根因
你的代码里存在两个严重的未定义行为,删GetStdHandle语句只是刚好改变了栈内存布局,暂时避开了崩溃触发点,并没有真正修复问题:
- 野指针访问:你定义的
CONSOLE_CURSOR_INFO *ConsoleCursorInfo是一个未初始化的指针,没有指向任何合法的CONSOLE_CURSOR_INFO结构体内存。你直接把这个野指针传给GetConsoleCursorInfo时,API会向这个指针指向的随机栈内存地址写入数据,直接破坏栈上的其他变量、函数返回地址等关键数据,这是崩溃的核心原因。手动只赋值2个数组元素时,代码执行路径短,被破坏的内存还没被访问到,所以看起来运行正常;跑全量for循环时访问到被破坏的内存段,就会直接触发程序终止。 - 变量未声明+常量拼写错误:代码里的
handleStandardOut没有声明类型,老版本C编译器会默认将其作为int类型处理,但HANDLE本质是void*类型,在64位系统下指针长度为8字节、int长度为4字节,赋值时会发生截断,进一步破坏栈内存布局。这也是为什么加/删这行代码会改变崩溃表现——这行代码的存在与否直接改变了栈上变量的排列位置,决定了野指针写入的脏数据会不会刚好碰到for循环要访问的内存区域。另外你代码里用的STD_OUT_HANDLE是错误的常量名,正确名称为STD_OUTPUT_HANDLE。
修复方案
按如下修改即可彻底解决问题:
- 不要用指针接收控制台光标信息,直接定义结构体变量即可,不需要手动分配内存
- 补全
handleStandardOut的类型声明,如果不用这个变量直接删掉即可 - 数组长度计算不要硬编码除以4的魔数,用
sizeof(数组首元素)计算,避免不同平台下结构体对齐差异导致的长度计算错误
修复后的核心代码片段:
int main(){ HANDLE handleNewScreenBuffer; // 直接定义结构体实例,不要用未初始化的指针 CONSOLE_CURSOR_INFO ConsoleCursorInfo; // 补全变量声明,不用的话可以直接删掉这行 HANDLE handleStandardOut; SMALL_RECT rectangle = {0, 0, 64, 7}; CHAR_INFO charInfo[520]; COORD coordBufSize = {65, 8}; COORD coordBufCoord = {0, 0}; handleStandardOut = GetStdHandle(STD_OUTPUT_HANDLE); handleNewScreenBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); // 传入结构体地址即可 GetConsoleCursorInfo(handleNewScreenBuffer, &ConsoleCursorInfo); ConsoleCursorInfo.bVisible = 0; SetConsoleCursorInfo(handleNewScreenBuffer, &ConsoleCursorInfo); // 正确计算数组长度,不要硬编码魔数 for (int x = 0; x < (sizeof(charInfo)/sizeof(charInfo[0])); ++x){ charInfo[x].Attributes = BACKGROUND_GREEN | FOREGROUND_RED; charInfo[x].Char.AsciiChar = (char)'&'; } WriteConsoleOutput(handleNewScreenBuffer, charInfo, coordBufSize, coordBufCoord, &rectangle); SetConsoleActiveScreenBuffer(handleNewScreenBuffer); Sleep(5000); return 0; }
内容的提问来源于stack exchange,提问作者Noah Braasch
相关产品推荐
相关产品推荐

