#1
  1. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    6
    Rep Power
    0

    Question segfault when assigning fields to a structure


    Help. This chunk of code is the problamatic part of a larger data base type program.

    It's task is to initialize two structures (first_blah, and last_blah), fill up their fields, and then print their fields to prove initialization worked.

    If I comment out one structure, it's initialization, and it's printing the program works fine. When I try to use two structures I get a segmentation fault.

    gbd tells me the segfault occurs in the first strcpy of the second structure initialization. Printing the values of the structure says that the fields of the second structure are inaccessable memory locations.

    I don't understand why one structure works fine, but two causes a segfault. I've looked at top, and I still have plenty of memory left.

    Thank you in advance for any help you are able to give.


    #include <stdio.h>
    #include <string.h>

    struct tenant {
    char last_name[80];
    char first_name[80];
    char street[80];
    int house_num;
    };

    struct tenant_list {
    struct tenant *the_tenant;
    struct tenant_list *next_tenant;
    struct tenant_list *prev_tenant;
    };

    int main(void)
    {
    /* linked list pointer moved into main()
    * to avoid usage of global variables */

    struct tenant_list *first_tenant_ptr;
    struct tenant_list *last_tenant_ptr;

    strcpy(last_tenant_ptr->the_tenant->last_name, "tail");
    strcpy(last_tenant_ptr->the_tenant->first_name, "tail");
    strcpy(last_tenant_ptr->the_tenant->street, "tail");
    last_tenant_ptr->the_tenant->house_num = 10000;
    last_tenant_ptr->next_tenant = NULL;
    last_tenant_ptr->prev_tenant = NULL;

    strcpy(first_tenant_ptr->the_tenant->last_name, "head");
    strcpy(first_tenant_ptr->the_tenant->first_name, "head");
    strcpy(first_tenant_ptr->the_tenant->street, "head");
    first_tenant_ptr->the_tenant->house_num = 0;
    first_tenant_ptr->next_tenant = NULL;
    first_tenant_ptr->prev_tenant = NULL;

    /*init_list(first_tenant_ptr, last_tenant_ptr);*/

    printf("%s\n", last_tenant_ptr->the_tenant->last_name);
    printf("%s\n", last_tenant_ptr->the_tenant->first_name);
    printf("%s\n", last_tenant_ptr->the_tenant->street);
    printf("%i\n", last_tenant_ptr->the_tenant->house_num);

    printf("%s\n", first_tenant_ptr->the_tenant->last_name);
    printf("%s\n", first_tenant_ptr->the_tenant->first_name);
    printf("%s\n", first_tenant_ptr->the_tenant->street);
    printf("%i\n", first_tenant_ptr->the_tenant->house_num);
    return 1;
    }
  2. #2
  3. Left due to despotic ad-min
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Jun 2003
    Posts
    1,044
    Rep Power
    14
    Your problem is the first couple of lines of main().

    Try initialising your variables first. eg

    first_tenant_ptr = malloc(sizeof tenant_list);
    first_tenant_ptr->the_tenant = malloc(sizeof tenant);

    /* etc etc */

    A raw declaration of a pointer, such as you have, declares a pointer but doesn't make it point at anything in particular. The result of dereferencing that pointer is formally called undefined behaviour. One possible symptom of undefined behaviour is a core dump.

    In your case, you're making it worse by effectively double dereferencing uninitialised pointers. first_tenant_ptr could point at anything. Even if it pointed at something sensible (i.e. if you initialised it), you need to initialise first_tenant_ptr->the_tenant to ensure it *also* points at something sensible.
  4. #3
  5. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    6
    Rep Power
    0
    Thank you for your response, that worked.

    So I guess that the other two pointers (the next_tenant and prev_tenant) don't need to be initialized as long they are pointed to something that has been initialized ( with malloc ) or NULL ?

    I don't think I completely understand.

    Initializing the inner structure ( tenant ) automatically initializes it's fields but initializing the structure holding it ( tenant_list ) does not initialize it's fields, in this case the structure pointer tenant ?

    Would it be good practise ( and correct ) to declare all pointers to declare to NULL ( for instance in the structure declaration )? This could then serve as a reminder that the pointer still needs to be initialized.

    Sorry to pick your brains, but pointer are melting mine.

    Thanks again.
  6. #4
  7. Left due to despotic ad-min
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Jun 2003
    Posts
    1,044
    Rep Power
    14
    I'm not going to answer your question directly, as to give a literal answer without explanation won't help you understand why. Work through the following and see if you can answer your own question.

    The basic rule which you don't seem to be getting is that any pointer variable (whether it is something you've declared or a member of a struct) is not guaranteed to be initialised to anything unless you explicitly initialise it. With some compilers you get lucky (eg the default initialisation is zero, or NULL) but you can't rely on that.

    So, when you declare a raw pointer

    struct tenant_list *first_tenant_ptr;

    what you have is a pointer. The actual value of the pointer (i.e. the address it contains) is not guaranteed to be anything in particular. So, for example, if we insert the lines

    assert(first_tenant_ptr == &some_valid_tenant_list);
    assert(first_tenant_ptr == (tenant_list *)NULL);

    [assert is a macro or function declared in one of the standard headers that terminates the program if the expression it receives is zero].

    With the declaration above, there is no guarantee that either of the assertions will fail. But, equally, there is no guarantee that either of them will succeed.

    When you allocate memory using malloc, things go along similar lines.

    first_tenant_ptr = malloc(sizeof struct tenant_list);

    What this does is point first_tenant_ptr at a area of dynamically allocated memory. However, the data in that memory block is not guaranteed to be initialised to anything either. So you can now dereference first_tenant_ptr safely (eg assign values to its data elements). But, unless you initialise the values, the data in the structure is not initialised. So, you also need to ensure that;

    first_tenant_ptr->the_tenant
    first_tenant_ptr->next_tenant
    first_tenant_ptr->prev_tenant

    are all initialised to something valid (eg the address of some existing object, the return value from malloc, NULL, etc).

    Note that I've said NULL is a valid value for a pointer. It is just a special value that indicates "no object".

    To take the example further, if we do ..

    first_tenant_ptr->the_tenant = malloc(sizeof struct tenant);

    the problem with initialisation continues. In this case, we can do

    strcpy(first_tenant_ptr->the_tenant->last_name, "Hello");

    quite safely (because last_name is actually an array). However, consider what happens if we do

    first_tenant_ptr = malloc(sizeof struct tenant_list);
    first_tenant_ptr->the_tenant = malloc(sizeof struct tenant);
    printf("%s\n", first_tenant_ptr->the_tenant->last_name);

    This example also potentially causes a crash (it may also print out garbage) because the bytes in first_tenant_ptr->the_tenant->last_name have not being initialised.

IMN logo majestic logo threadwatch logo seochat tools logo