C语言ncurses中set_menu_format的row参数是否存在硬性限制?
我正在编写一个基于菜单的C语言ncurses程序,已确保set_menu_format的row参数小于子窗口尺寸,但无法将row参数设置为大于21的值。通过getmaxyx获取到终端的最大行数为35,当row参数超过21时,程序会抛出“free(): invalid pointer”错误并出现“zsh: IOT instruction(core dumped)”核心转储。
相关代码片段
核心问题代码
set_menu_win(my_menu, my_menu_win[0]); set_menu_sub(my_menu, derwin(my_menu_win[0],29,(ncols/3)-2,1,1)); set_menu_format(my_menu, 21, 1);
菜单遍历函数
void traverse(MENU *my_menu,WINDOW **my_menu_win,ITEM **my_items,int argc) { int c,cur_win_index=0; while((c=getch()) != 'q') { ITEM *cur_item = current_item(my_menu); switch(c) { case 'j': if(cur_item == my_items[argc-2]) menu_driver(my_menu, REQ_FIRST_ITEM); else menu_driver(my_menu, REQ_DOWN_ITEM); break; case 'k': if(cur_item == my_items[0]) menu_driver(my_menu, REQ_LAST_ITEM); else menu_driver(my_menu, REQ_UP_ITEM); break; } wrefresh(my_menu_win[0]); } }
菜单显示函数
void menu_display(int argc, char *argv[]) { // Declaration // ITEM **my_items; MENU *my_menu; WINDOW *my_menu_win[3], *title_win; PANEL *my_panels[3]; char store[argc-1][4]; int c,i; char search; int nlines,ncols,startx,starty; // Declaration // // Initialize curses // initscr(); start_color(); cbreak(); noecho(); curs_set(0); keypad(stdscr,TRUE); init_pair(1, COLOR_RED, COLOR_BLACK); // Initialize curses // // Initialize Variables // getmaxyx(stdscr, nlines, ncols); starty = 0; startx = 0; // Initialize Variables // // Create Menu Items // my_items = (ITEM **)calloc(argc, sizeof(ITEM *)); for(i=1;i<argc;i++) { sprintf(store[i-1],"%d",i); my_items[i-1] = new_item(store[i-1], argv[i]); } my_items[argc] = (ITEM *)NULL; // Create Menu Items // // Create Menu // my_menu = new_menu((ITEM**)my_items); // Create Menu // // Create windows and panels // title_win = newwin(2, ncols, starty, startx); my_menu_win[0] = newwin(30,ncols/3,starty+3,startx); my_menu_win[1] = newwin(30,ncols/3,starty+3,startx+(ncols/3)); my_menu_win[2] = newwin(30,ncols/3,starty+3,3*startx+(2*ncols/3)); for(i=0;i<3;i++) my_panels[i] = new_panel(my_menu_win[i]); // Create windows and panels // // sets menu main and sub windows ; sets menu mark// set_menu_win(my_menu, my_menu_win[0]); set_menu_sub(my_menu, derwin(my_menu_win[0],29,(ncols/3)-2,1,1)); set_menu_format(my_menu, 21, 1); set_menu_mark(my_menu, "*"); // mark strings to show selected items // sets menu main and sub windows ; sets menu mark// // Print border around main window; add title// print_title(title_win, 1, startx, ncols, "File Manager", COLOR_PAIR(1)); mvwprintw(title_win, 0, 0, "%d %d", nlines, ncols); for(i=0;i<3;i++) box(my_menu_win[i], 0, 0); // Print border around main window and title // // Refresh title window// wrefresh(title_win); // Refresh title window// // Post the menu; refresh panels // post_menu(my_menu); update_panels(); doupdate(); // Post the menu; refresh panels // // Traverse Menu Items // traverse(my_menu, my_menu_win, my_items, argc); // Traverse Menu Items // // Free memory // unpost_menu(my_menu); free_menu(my_menu); for(i=0;i<argc;i++) free_item(my_items[i]); endwin(); // Free memory // }
问题分析与解决方案
set_menu_format本身没有硬性的row参数上限,它的限制仅来自于你设置的菜单子窗口尺寸(子窗口必须能容纳指定的行数和列数)。你的崩溃问题并非参数限制导致,而是内存越界和无效指针访问引发的。
关键问题点
菜单项数组越界
你分配了argc个指针的数组my_items,但仅初始化了my_items[0]到my_items[argc-2](共argc-1个项),却直接将my_items[argc]设为NULL,跳过了my_items[argc-1]。这个未初始化的垃圾指针会导致后续菜单操作访问无效内存。内存释放错误
释放菜单项时,循环遍历0到argc-1,但实际有效的菜单项只有argc-1个(索引0到argc-2)。尝试释放my_items[argc-1]这个未初始化的指针,直接触发“free(): invalid pointer”错误,也就是你看到的崩溃。
修复步骤
修正菜单项终止标记
将my_items[argc] = (ITEM *)NULL;改为my_items[argc-1] = (ITEM *)NULL;,确保数组终止符位置正确。调整内存释放范围
将释放循环改为:for(i=0;i<argc-1;i++) free_item(my_items[i]);只释放实际创建的菜单项,避免访问无效指针。
验证子窗口可用空间
确认子窗口的实际可用行数:你创建的子窗口高度为29,去掉上下边框后实际可用行数为27,因此set_menu_format的row参数设为22及以下都是符合要求的,修正内存问题后即可正常使用更大的row值。
内容的提问来源于stack exchange,提问作者Shreyas Jenner

