Как предотвратить висячие указатели / мусор в c? - PullRequest
0 голосов
/ 17 января 2012

Я новичок в C и не совсем понял, когда C решает освободить объект и когда он решает сохранить объект.

heap_t - указатель на кучу структур.

heap_t create_heap(){
    heap_t h_t = (heap_t)malloc(sizeof(heap));
    h_t->it = 0;
    h_t->len = 10;
    h_t->arr = (token_t)calloc(10, sizeof(token));
    //call below a couple of times to fill up arr
    app_heap(h_t, ENUM, "enum", 1);
    return h_t;
}

пропуская h_t через

int app_heap(heap_t h, enum symbol s, char* word, int line){
    int it = h->it;
    int len = h->len;

    if (it + 1 < len ){
        token temp;
        h->arr[it] = temp;
        h->arr[it].sym = s;
        h->arr[it].word = word;
        h->arr[it].line = line;
        h->it = it + 1;
        printf(h->arr[it].word);
        return 1;
    } else {
        h->len = len*2;
        h->arr = realloc(h->arr, len*2);
        return app_heap(h, s, word, line);
    }

}

Почему мой h_t-> arr заполняется мусором, и в итоге я получаю ошибку сегментации?Как это исправить?Любые советы / стили C-кодирования, чтобы избежать подобных вещей?

Ответы [ 4 ]

1 голос
/ 17 января 2012

Во-первых, чтобы ответить на ваш вопрос о сбое, я думаю, причина того, что вы получаете ошибку сегментации, заключается в том, что вам не удалось умножить len на sizeof(token) при вызове realloc.Вы заканчиваете тем, что пишете после конца блока, который был выделен, в конечном счете вызывая segfault.

Доходит "решение освободить объект и когда [...] сохранить объект", Cне решает ничего за вас: он просто делает это, когда вы говорите об этом по телефону free, не задавая вам никаких дополнительных вопросов.Это «послушание» иногда приводит к тому, что вы стоите, потому что вы можете случайно освободить то, что вам все еще нужно.Хорошей идеей будет обнулить указатель, чтобы улучшить ваши шансы на быстрое обнаружение проблемы (к сожалению, этого недостаточно для полного устранения проблемы из-за общих указателей).

free(h->arr);
h -> arr = NULL; // Doing this is a good practice

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

1 голос
/ 17 января 2012

C ничего не «решает», если вы сами что-то выделили с помощью явного вызова, например, malloc(), оно останется выделенным, пока вы не free() (или, как правило, до завершения программы).

Я думаю, что:

   token temp;
   h->arr[it] = temp;
   h->arr[it].sym = s;
   /* more accesses */

очень странно, первые две строки не делают ничего разумного.

Как указывает dasblinkenlight , вывам не удается масштабировать перераспределение в байты, что приведет к резкому сокращению массива при попытке его увеличения и полному повреждению.

  • Не следует приводить возвращаемые значения malloc() и realloc(), в C.
  • Помните, что realloc() может потерпеть неудачу, и в этом случае вы потеряете указатель, если перезапишите его, как делаете.
  • Много повторений вваш код, то есть realloc(h->arr, len*2) вместо realloc(h->arr, h->len * sizeof *h->arr) и т. д.

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

0 голосов
/ 25 июля 2016

Две основные проблемы при создании висячих указателей в C - это не присваивание NULL указателю после освобождения его выделенной памяти, и общие указатели.

Существует решение первой проблемы - автоматического обнуленияуказатель.

void SaferFree(void *AFree[])
{
    free(AFree[0]);
    AFree[0] = NULL;
}

Вызывающий абонент, вместо вызова

free(p);

, вызовет

SaferFree(&p);

По второму и более сложному вопросу: Правило трех гласит:

Если вам нужно явно объявить деструктор, конструктор копирования или оператор копирования, вам, вероятно, нужно явно объявить все три из них.

Совместное использование указателя в C просто копирует его (назначение копирования).Это означает, что использование правила трех (или общего правила 0) при программировании на C обязывает программиста предоставить способ создания и, в частности, уничтожения такого назначения, что возможно, но не является легкой задачей, особенно когда C не делает этого.предоставить дескриптор, который неявно активируется, как в C ++.

0 голосов
/ 17 января 2012

Вы не перераспределяете нужный размер, оператор realloc должен быть:

    realloc(h->arr, sizeof(token) * len*2);
                    ^^^^^^^^^^^^  

(или, возможно, лучше realloc(h->arr, sizeof *h->arr * h->h_len);)

В C вы несете ответственность заосвободить память, которую вы выделяете.Вы должны освободить () память, которую вы используете malloc / calloc / realloc, когда это необходимо.Среда выполнения C никогда ничего не освобождает, кроме случаев, когда программа завершена (некоторые более эзотерические системы могут даже не освободить память).

Кроме того, старайтесь быть последовательными, общая форма распределения всегда T *foo = malloc(sizeof *foo)и не дублируйте вещи.

например,

h_t->arr = (token_t)calloc(10, sizeof(token));
            ^^^^^^^^       ^^   ^^^^^^^^^^^^^
  1. Не приводите возвращаемое значение malloc в C. Это не нужно и может скрыть серьезное предупреждение компилятора и ошибку, если выне забудьте включить stdlib.h
  2. приведение к типу token_t, но sizeof применяется к токену, почему они отличаются и имеют тот же тип, что и * h_t-> arr?
  3. У вас уже естьмагическое значение 10, используйте h_t-> len
  4. Если вы когда-либо измените тип h_t-> arr, вы должны помнить, чтобы изменить sizeof (..)

Таксделать это

  h_t->arr = calloc(h_t->len, sizeof *h_t->arr);
...