开发者

pointer to a pointer to a struct giving headache . .

I'm not sure how to explain this but this piece of code bellow can compile perfectly but when you run it, SIGSEV. Please, can anyone tell precisely where I got things wrong? The fact is I want to be able to access elements by index as below and at the same time to be able to wor开发者_StackOverflow中文版k with struct.

#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;
}


The problem is in the insert method at the line of code you flagged in the question

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

Nowhere in your program do you allocate memory for the p array inside of the person struct. Hence this value will always be NULL. Attempting to assign to this value will rightfully lead to a crash of your program.

To fix this you need to ensure the p array is large enough to accommodate the index provided by the expression sample->id. Best way to accomplish this is to use the realloc function and add a field to person to store the size of the p array

Here's a quick sample. Note: Error checking and 0 initialization of memory omitted for 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);
  }
  ...
}

It does seem odd though that the name and age are always indexed via the sample->id field. This indicates that it's always placed in the same location in which case an array is not needed. Can you elaborate on how this is supposed to function?


In your init() function you set sample->p = NULL. In your insert() function you try to dereference the ->p member sample->p[sample->id]->name . Since you've not pointed ->p to any storage, you can't dereference it.


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 is not being properly initialized. If you look in init, it is indeed initialized to NULL. sample->p[anything] therefore dereferences a null pointer, causing a segfault.


When you call your init() function, you allocate memory for a number of person structures, and set the 'p' pointer of the first structure to NULL.

Then you try to write the memory pointed by 'p'. Which is, of course, still NULL.

Given your comments, I don't think init() is doing what you want it to do. It's allocating space for a person structure array, rather than a person with a 'p' array. Also, why the double pointer?

Recheck your design :) I usually do it in a whiteboard, or pencil and paper, using boxes for my 'objects' and arrows for my pointers. It will clarify your ideas, and possibly show you mistakes before they ever reach the code.


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;
}

And now, these two statements are valid -

sample->p[sample->id]->name = _name;
sample->p[sample->id]->age = _age;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜