C Указатель и распределение памяти: массивы Realloc и прохождение указателя - PullRequest
0 голосов
/ 25 февраля 2012

Для тех, кто имеет опыт работы с C, это будет простая проблема выделения / обращения к памяти:

Вот мои структуры данных:

struct configsection {
    char *name;
    unsigned int numopts;
    configoption *options;
};
typedef struct configsection configsection;

struct configfile {
    unsigned int numsections;
    configsection *sections;
};
typedef struct configfile configfile;

Вот мои процедуры для инициализации раздела конфигурации или файла конфигурации и для добавления раздела конфигурации в файл конфигурации:

// Initialize a configfile structure (0 sections)
void init_file(configfile *cf) {
    cf = malloc(sizeof(configfile));
    cf->numsections = 0;
}
// Initialize a configsection structure with a name (and 0 options)
void init_sec(configsection *sec, char *name) {
    sec = malloc(sizeof(configsection));
    sec->numopts = 0;
    sec->name = name;
    printf("%s\n", sec->name);
}
// Add a section to a configfile
void add_sec(configfile *cf, configsection *sec) {
    // Increase the size indicator by 1
    cf->numsections = cf->numsections + 1;
    // Reallocate the array to accommodate one more item
    cf->sections = realloc(cf->sections, sizeof(configsection)*cf->numsections);
    // Insert the new item
    cf->sections[cf->numsections] = *sec;
}

Я полагаю, что моя проблема возникла в моей функции init_sec (). Вот пример:

int main(void) {

// Initialize test configfile
configfile *cf;
init_file(cf);

// Initialize test configsections
configsection *testcs1;
init_sec(testcs1, "Test Section 1");
// Try printing the value that should have just been stored
printf("test name = %s\n", testcs1->name);

Хотя printf() в init_sec() успешно печатает имя, которое я только что сохранил в разделе конфигурации, попытка того же самого в printf() из main() вызывает ошибку сегментации. Кроме того, addsec() вызывает ошибку сегментации.

Ответы [ 5 ]

2 голосов
/ 25 февраля 2012

Вы ни в коем случае не инициализируете cf->sections, что означает, что при первой попытке realloc вы пропускаете мусор.Добавление:

 cf->sections = NULL;

к init_file должно помочь.

Вы также не проверяете коды возврата, но знаете, что да?

2 голосов
/ 25 февраля 2012

Эта подпрограмма должна быть

void init_file(configfile **cf) { 
    *cf = malloc(sizeof(configfile)); 
    (*cf)->numsections = 0;
    (*cf)->sections = NULL; // You forgot to initialise this.
}

, то есть вызываться с помощью init_file(&myconfigfilepointer);, поэтому возвращаемое значение malloc возвращается.

Необходимо сделать тот же трюк для init_sec

Эта функция неверна - здесь исправленная версия

void add_sec(configfile *cf, configsection *sec) {     
    // Increase the size indicator by 1     
    // Reallocate the array to accommodate one more item     
    cf->sections = realloc(cf->sections, sizeof(configsection)*(1 + cf->numsections));     
    // Insert the new item     
    cf->sections[cf->numsections] = *sec; // Since arrays start at 0     
    cf->numsections = cf->numsections + 1;     
} 

Затем необходимо настроить вызовы в main

1 голос
/ 25 февраля 2012

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

Хотя вы можете решить проблему, передав указатель науказатель вызывающей стороны (то есть двойной указатель), это не обязательно самый элегантный или самый обычный способ обработки вещей.Скорее, вы должны вернуть результат выделения из функции.Пока вы работаете с этим, вы также должны использовать calloc, чтобы сразу обнулить память.Подводя итоги:

typedef struct substuff_
{
    int a;
    double b;
} substuff;

typedef struct stuff_
{
    unsigned int n;
    substuff * data;
} stuff;

substuff * init_substuff()
{
    substuff * const p = malloc(sizeof *p);
    if (p) { p->a = 5; p->b = -0.5; }
    return p;
}

stuff * init_stuff()
{
    substuff * const p = init_substuff();
    if (!p) return NULL;

    stuff * const q = malloc(sizeof *q);
    if (q) { q->n = 10; q->data = p; }
    return q;
}

В качестве упражнения вы должны написать соответствующие функции void free_substuff(substuff *) и void free_stuff(stuff *).

1 голос
/ 25 февраля 2012

Да, есть проблема в init_sec

// Initialize a configsection structure with a name (and 0 options)
void init_sec(configsection *sec, char *name) {
    sec = malloc(sizeof(configsection));
    sec->numopts = 0;
    sec->name = name;
    printf("%s\n", sec->name);
}

Вы просто копируете указатель имени здесь, что означает, что он указывает на оригинальное хранилище имени. Если бы вы позвонили init_sec, как это

configsection foobar()
{
    configsection sec;
    char name[80];

    get_name(name);
    init_sec(sec, name);
    return sec;    
}

Указатель name стал недействительным в момент возврата foobar. Вам нужно продублировать строку и сохранить вашу личную копию. В init_sec:

    sec->name = strdup(name);

Но это еще не все. В самой первой строке init_sec вы перезаписываете указатель, который был передан в init_sec, указателем malloc. Таким образом, новый указатель никогда не передается обратно в вызов. Либо используйте указатель на указатель, вообще не берите указатель configsection (в конце концов, вы выделяете), а просто возвращайте выделенный указатель: Полная исправленная функция:

// Initialize a configsection structure with a name (and 0 options)
configsection* init_sec(char *name) {
    configsection *sec = malloc(sizeof(configsection));
    sec->numopts = 0;
    sec->name = name;
    printf("%s\n", sec->name);
    return sec;
}
1 голос
/ 25 февраля 2012

Вам нужно передать указатель значения, которое будет обновлено ... например:

// Initialize a configfile structure (0 sections)
void init_file(configfile **cf) {
    *cf = malloc(sizeof(configfile));
    (*cf)->numsections = 0;
}

configfile *var;
init_file(&var);
printf("%d\n", var->numsections);

В противном случае вы просто обновляете локальный указатель * cf, а не оригинал, переданный в значении

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...