указатель на указатель на структуру, вызывающую головную боль., - PullRequest
4 голосов
/ 22 марта 2011

Я не уверен, как это объяснить, но этот фрагмент кода может скомпилироваться отлично, но когда вы запустите его, SIGSEV.Пожалуйста, кто-нибудь может точно сказать, где я ошибся?Дело в том, что я хочу иметь доступ к элементам по индексу, как показано ниже, и в то же время иметь возможность работать со структурой.

#include <stdio.h>
#include <stdlib.h>

/* This is a struct describing properties of an element */
struct element{
    int age;
    char* name;
};

/* This struct contains a pointer to a pointer on a element "struct element" */
struct person{
    struct element** p;
    int id;
};

/* Thus function initializes a struct person by allocation memory for it */
struct person* init(int size)
{
    struct person* sample = (struct person* )malloc(size*sizeof(struct person));
    sample->p = NULL;
    sample->id = 0;
    return sample;
}

/* use this function to insert a new element in the struct */
void insert(struct person* sample, char* _name, int _age)
{
    sample->p[sample->id]->name = _name; /* the program crashes here  according to the debugger , but why?? */
    sample->p[sample->id]->age = _age;  /* of course, this will cause trouble too because it has the same construct as the previous one */
    sample->id++;
}


/* main entry */
int main()
{
    struct person* student = init(10); /* Allocating space for 10 students */
    insert(student, "kido", 8);
    printf("Your name is %s and your age is %d", student->p[0]->name, student->p[0]->age); /* we can't write student->p->name */
    return 0;
}

Ответы [ 5 ]

5 голосов
/ 22 марта 2011

Проблема в методе insert в строке кода, отмеченной вами в вопросе

sample->p[sample->id]->name = _name;

Нигде в вашей программе вы не выделяете память для массива p внутри person структура.Следовательно, это значение всегда будет NULL.Попытка присвоить это значение по праву приведет к краху вашей программы.

Чтобы исправить это, необходимо убедиться, что массив p достаточно большой, чтобы вместить индекс, предоставленный выражением sample->id.Лучший способ сделать это - использовать функцию realloc и добавить поле к person для хранения размера массива p

Вот краткий пример.Примечание: проверка ошибок и 0 инициализация памяти опущены для bevity.

struct person{
    struct element** p;
    size_t length;
    int id;
};

void insert(struct person* sample, char* _name, int _age)
{
  if (sample->id >= sample->length) {
    sample->p = realloc(sample->p, sizeof(element*) * sample->id);
  }
  ...
}

Кажется странным, что имя и возраст всегда индексируются через поле sample->id.Это указывает на то, что он всегда находится в одном и том же месте, и в этом случае массив не нужен.Можете ли вы уточнить, как это должно функционировать?

0 голосов
/ 22 марта 2011
struct person* init(int size)
{
    struct person* sample = (struct person* )malloc(size*sizeof(struct person));
    sample->p = NULL; // p is a pointer to a pointer which is initialized to NULL
                      // So, it cannot be dereferenced with out pointing to a valid
                      // memory location.

    // sample -> p = (struct person**) malloc( sizeof(struct *person) );
    // sample[p] = (struct(person*)) malloc( sizeof(struct person) );

    // struct** -> struct* -> struct

    sample->id = 0;
    return sample;
}

И теперь эти два утверждения верны -

sample->p[sample->id]->name = _name;
sample->p[sample->id]->age = _age;
0 голосов
/ 22 марта 2011

Когда вы вызываете функцию init(), вы выделяете память для ряда person структур и устанавливаете указатель 'p' первой структуры в NULL.

Затем вы пытаетесь записать память, указанную как 'p'. Который, конечно, по-прежнему NULL.

Учитывая ваши комментарии, я не думаю, что init () делает то, что вы хотите. Он выделяет пространство для массива структуры person, а не для лица с массивом 'p'. Кроме того, почему двойной указатель?

Перепроверьте свой дизайн :) Я обычно делаю это на доске, карандаше или бумаге, используя коробки для моих «объектов» и стрелки для моих указателей. Он прояснит ваши идеи и, возможно, покажет вам ошибки еще до того, как они дойдут до кода.

0 голосов
/ 22 марта 2011
Starting program: /home/nathan/c/seg 

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400597 in insert (sample=0x601010, _name=0x400730 "kido", _age=8)
    at seg.c:28
28          sample->p[sample->id]->name = _name; /* the program craches here  according to the debugger , but why?? */
(gdb) backtrace
#0  0x0000000000400597 in insert (sample=0x601010, _name=0x400730 "kido", 
    _age=8) at seg.c:28
#1  0x0000000000400601 in main () at seg.c:38
(gdb) p sample->id
$1 = 0
(gdb) p sample->p
$2 = (struct element **) 0x0

sample->p неправильно инициализируется. Если вы посмотрите на init, оно действительно инициализируется на NULL. sample->p[anything] поэтому разыменовывает нулевой указатель, вызывая ошибку.

0 голосов
/ 22 марта 2011

В вашей функции init () вы устанавливаете sample->p = NULL. В вашей функции insert () вы пытаетесь разыменовать член -> p sample->p[sample->id]->name. Поскольку вы не указали -> p на любое хранилище, вы не можете разыменовать его.

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