Почему ошибка сегментации происходит только тогда, когда массив выбора состоит из 3 или 7 элементов? - PullRequest
2 голосов
/ 24 апреля 2019

Я новичок в C и пытаюсь разработать небольшое программное обеспечение для ncurses с интерфейсом на основе меню.У меня есть этот код, который http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/menus.html.

Я не понимаю, почему, если я инициализирую массив выбора из 3 или 7 элементов, программа переходит на segfault, но хорошо, если массив любого другого размера.

Вот код.Если добавить 1 к вызову n_choices или calloc (), то ошибки сегментации не произойдет.то есть

my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));

Почему 3 и 7 "особенные" ???

#include <ncurses.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <menu.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

WINDOW *mywin;
int rows, cols, h, w, starty, startx;
int wybeg, wxbeg, wymax, wxmax;

// choices
char *choices[] = {
    "Choice 1",
    "Choice 2",
    "Choice 3",
    "Choice 4",
    "Choice 5",
    "Choice 6",
    "Choice 7",
};
void func(char*);

int main(int argc, char** argv)
{
    ITEM **my_items;
    int c;              
    MENU *my_menu;
    int n_choices, i;
    ITEM *cur_item;

    initscr(); //creates stdscr
    cbreak();
    noecho();
    curs_set(0);
    keypad(stdscr, TRUE);

    if(has_colors() == TRUE){
        start_color();
        init_pair(1,COLOR_YELLOW,COLOR_BLUE);
    }

    getmaxyx(stdscr, rows, cols);
    h = rows-4;
    w = cols;
    starty = 1;
    startx = 0;

    char quitHint[] = "<Press q to exit>";

    move(0,0);
    mvprintw(LINES-1, COLS-1-strlen(quitHint), "%s", quitHint);
    refresh();

    mywin = newwin(h,w,starty,startx);
    keypad(mywin,TRUE);
    box(mywin,0,0);
    wbkgd(mywin,COLOR_PAIR(1));
    wrefresh(mywin);

    // MENU
    /* Create items */
    /**************************/
    /* FROM HERE IS THE POINT */
    /**************************/
    // if choices is 3 or 7 elements, segfault occurs! If adding +1 n_choices segfault doesn't occur
    n_choices = ARRAY_SIZE(choices);
    my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
    for(i = 0; i < n_choices; ++i){
        my_items[i] = new_item(choices[i], choices[i]);
        /* Set the user pointer */
        set_item_userptr(my_items[i], func);
    }

    mvprintw(LINES-2,2,"sizeof(choices) = %3d | sizeof(*choices) = %3d | n_choices = %3d", sizeof(choices), sizeof(*choices), n_choices);

    /* Create menu */
    my_menu = new_menu((ITEM **)my_items);

    /* Set main window and sub window */
    set_menu_win(my_menu, mywin);
    set_menu_sub(my_menu, derwin(mywin, n_choices, 38, 2, 2));

    /* Set menu mark to the string " * " */
    set_menu_mark(my_menu, " * ");
    refresh();

    set_menu_fore(my_menu, COLOR_PAIR(1) | A_REVERSE);  // selected
    set_menu_back(my_menu, COLOR_PAIR(1));                          // unselected

    /* Post the menu */
    post_menu(my_menu);
    wrefresh(mywin);

    while((c = wgetch(mywin)) != 'q')
    {
        switch(c)
        {   case KEY_DOWN:
                menu_driver(my_menu, REQ_DOWN_ITEM);
                break;
        case KEY_UP:
            menu_driver(my_menu, REQ_UP_ITEM);
            break;
        case 10: /* enter */
        {
            /* execute func() from item pointer */
            ITEM *cur;
            void (*p) (char *);

            cur = current_item(my_menu);
            p = item_userptr(cur);
            p((char *)item_name(cur));
            pos_menu_cursor(my_menu);
            break;
        }
        }
        wrefresh(mywin);
    }

    /* Unpost and free all the memory taken up */
    unpost_menu(my_menu);
    free_menu(my_menu);
    for(i = 0; i < n_choices; ++i){
        free_item(my_items[i]);
    }

    endwin();
    return EXIT_SUCCESS;
}

void func(char *local_choice){  
    mvwprintw(mywin,h-2, 2, "Item selected is : %s", local_choice);
    wclrtoeol(mywin);
    box(mywin,0,0);
    wrefresh(mywin);
}   

Скомпилировано с gcc -lmenu -lncurses

Запуск Debian Buster 4.19.0-2-686 #1 SMP Debian 4.19.16-1 (2019-01-17) i686 GNU/Linux

1 Ответ

2 голосов
/ 24 апреля 2019

Может быть, у автора возникла та же ошибка и он решил выделить дополнительную память? Но почему 3 и 7 являются «особыми» размерами и запускают segfault?

Нет, это требуется с помощью new_menu функция:

Функция new_menu ожидает, что список items будет завершен элементом NULL.

Если вы прочитали документацию new_menu ( menu_new.3x.html ), вы увидите:

СИНТАКСИС

#include <menu.h>
MENU *new_menu(ITEM **items);

ОПИСАНИЕ

The function new_menu creates a new menu connected to a specified item
pointer array(which must be NULL-terminated).


Таким образом, сбой, который вы видите в случаях 3 и 7, - я думаю, связан с тем, как хранятся данные в памяти. Одна вещь, которую вы можете сделать, - выгрузить my_items memory + one element в файл непосредственно перед вызовом функции new_menu.

Я думаю, у вас будет какое-то значение 0 сразу после элементов, но не в 3 и 7 случаях. (Мое предположение какой-то 4-кратный эффект локализации)

...