Распределение памяти в приложении C

В настоящее время я пишу небольшое приложение для повторного ознакомления с C (прошло некоторое время с тех пор, как я в последний раз писал что-либо), и, как и большинство людей, я столкнулся с проблемой распределения памяти, которую я не могу понять.

Код вращается вокруг настройки различных панелей, окон и связанных с ними заголовков. Именно с выделением памяти для строки заголовка у меня проблемы.

База представляет собой структуру, которая содержит:

struct TFB_PANEL
{
    WINDOW *window;
    char *title;
};

Это typedef'd в заголовочном файле как:

typedef struct TFB_PANEL TfbPanel;

В соответствующем файле C у меня есть следующий метод, который инициализирует массив TFB фиксированного размера.

int tfb_init()
{
    if (!_initialised) {
        return -1;
    }

    int i;
    for (i = 0; i < TYPE_MAX; i++) {
        TFB[i] = malloc(sizeof(TfbPanel*));
        TFB[i]->title = calloc((strlen(TYPES[i]) + 18), sizeof(char*));
        switch(i)
        {
            case A:
                sprintf(TFB[i]->title, " %s | r | h | s | t ", TYPES[i]);
                break;
            case B:
                sprintf(TFB[i]->title, " f | %s | h | s | t ", TYPES[i]);
                break;
            case C:
                sprintf(TFB[i]->title, " f | r | %s | s | t ", TYPES[i]);
                break;
            case D:
                sprintf(TFB[i]->title, " f | r | h | %s | t ", TYPES[i]);
                break;
            case E:
                sprintf(TFB[i]->title, " f | r | h | s | %s ", TYPES[i]);
                break;
        }

        TFB[i]->window = tfb_create_window(i);
    }
    return 0;
}

Теперь ошибка возникает при инициализации C. A и B правильно установлены на длину 25 символов. C, с другой стороны, после инициализации должен содержать 24 символа, но вместо этого (изнутри tfb_create_window) я получаю

Program received signal EXC_BAD_ACCESS, Could not access memory
Reason: 13 at address 0x0000000000000000
0x00007fff930b9390 in strcmp()

Изучение стека показывает, что TFB[C] инициализируется правильно, но элемент заголовка — нет. Это содержит нулевой элемент, как будто я никогда не вызывал calloc.

Пожалуйста, может кто-нибудь объяснить, где я ошибаюсь или почему A и B инициализируются правильно, но C убивает приложение. Все шло прекрасно примерно до 6 утра этим утром, с тех пор все шло под откос.

Если это поможет, tfb_create_window определяется вызовом _create_window и вызовом tfb_get_title следующим образом:

char *tfb_get_title(int type)
{
    if (type >= TFB_MAX) {
        return (char*)NULL;
    }
    return TFB[type]->title;
}

WINDOW *tfb_create_window(int type)
{
    int height = ((LINES - WIN_OFFSET_Y) / 3);
    char *title = tfb_get_title(type);
    return _create_window(height, WIN_SIDEBAR_X, WIN_OFFSET_Y, 0, COLOUR_MAIN, title);
}

WINDOW *_create_window(int height, int width, int starty, int startx, int color, const char *title)
{
    WINDOW *window;
    window = newwin(height, width, starty, startx);
    box(window, 0, 0);
    mvwprintw(window, 0, 2, title);
    wbkgd(window, COLOR_PAIR(color));
    return window;
}

person mplf    schedule 14.09.2013    source источник
comment
Это не ваша проблема, но учтите, что вы на самом деле выделяете слишком много памяти в какой-то момент: calloc((strlen(TYPES[i]) + 18), sizeof(char*)). Это выделяет место для (strlen(TYPES[i]) + 18 char указателей, что, вероятно, в 4 или 8 раз больше памяти, чем вам действительно нужно.   -  person svk    schedule 14.09.2013
comment
Я так и думал, я изначально выделял только strlen(TYPES[i]) + 18, но я добавил множитель, пытаясь это исправить. Я также пытался просто использовать malloc(strlen(TYPES[i]) + 18), но безрезультатно.   -  person mplf    schedule 14.09.2013
comment
@mplf Вы уверены, что хотите сделать это malloc(sizeof(TfbPanel*)) вместо malloc(sizeof(TfbPanel))? Кроме того, что такое TFB[i]?   -  person Uchia Itachi    schedule 14.09.2013
comment
Память для TfbPanel не является проблемой, независимо от того, каким образом я выделяю память для панели, я все равно получаю тот же самый плохой сигнал доступа из сравнения строк. TFB[i] — это указатель на действительный блок памяти, который раньше работал нормально, пока я не добавил динамический заголовок. Массив TFB определяется как: TfbPanel *TFB[TYPE_MAX]   -  person mplf    schedule 14.09.2013
comment
Я имею в виду, что вы хотите, чтобы размер указателя был вашим размером объекта TfbPanel?   -  person Uchia Itachi    schedule 14.09.2013
comment
Я думаю, что это отдельная проблема, которая омрачает реальную проблему, которую я пытаюсь решить. Я изменил его на использование размера struct TfbPanel, и я все еще вижу ту же проблему.   -  person mplf    schedule 14.09.2013
comment
А как насчет sizeof(char*) до sizeof(char) ?   -  person Uchia Itachi    schedule 14.09.2013
comment
Точно такая же проблема и здесь :/   -  person mplf    schedule 14.09.2013


Ответы (2)


В этом коде есть ряд ошибок, самая серьезная из которых заключается в том, что вы используете sizeof(Type*) вместо sizeof(Type). Последний - это фактический размер объекта, первый - просто размер указателя на этот объект (который составляет 8 байтов на 64-битных машинах).

Это означает, что malloc(sizeof(TfbPanel*)) выделил слишком мало памяти для вашего TfbPanel, поэтому остальная часть программы ведет себя неопределенно.

calloc() точно так же неверно, но некритично, потому что вы выделяете в восемь раз больше памяти, чем вам нужно.

Однако в вызове calloc() есть еще одна ошибка: вы добавляете 18 к результату strlen(), то есть количеству символов полезной нагрузки, которые вы добавляете в свои вызовы sprintf(). Это слишком мало, потому что результат strlen() не включает нулевой байт.

Если вы можете использовать совместимую с POSIX-2008 libc (например, glibc в системах Linux), вы можете использовать asprintf() вместо sprintf(), она автоматически выделяет достаточно места для результирующей строки, избегая любых возможных ошибок размера буфера. Если вы не можете использовать эту функцию, используйте как минимум snprintf(), чтобы избежать доступа к нераспределенной памяти.

person cmaster - reinstate monica    schedule 14.09.2013
comment
Основываясь на комментариях, я уже заменил назначение malloc(sizeof(TfbPanel*)) на использование malloc(sizeof(struct TfbPanel)) - ключевой проблемой, как вы правильно указали, было слишком мало символов в строке. Я добавил это, и это решено. Спасибо. - person mplf; 14.09.2013

Если вы перейдете с помощью gdb к этой строке:

sprintf(TFB[i]->title, " f | r | %s | s | t ", TYPES[i]);

Каковы значения TYPES[i]?

person ioseph    schedule 14.09.2013
comment
TYPES — это фиксированный массив, определенный как char *TYPE[] = {feature, release, hotfix, support, trunk}; - person mplf; 14.09.2013
comment
каково значение TYPE_MAX? - person varnie; 14.09.2013
comment
enum в диапазоне от A до TYPE_MAX, соответствующих значениям ключа из массива TYPE - person mplf; 14.09.2013