Search code examples
cpointersstructreferencedereference

simple address book program with struct in C


I have a problem related with this program :

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

typedef struct people
{
    char full_name [100];
    char age [100];
    char phone_number [100];

} Person;

typedef struct address
{
    Person *person_list;

} Address_book;

void print_person(const Person* person)
{
    printf("full_name    : %s \n", person->full_name);
    printf("age          : %s \n", person->age);
    printf("phone_number : %s \n", person->phone_number);
}

void getinput (char *string)
{
    ssize_t read = 0;
    char *line = NULL;
    size_t n = 0;
    read = getline (&line, &n, stdin);
    if (line[read - 1] == '\n')
        { line[read - 1] = 0; read--; }
    strncpy (string, line, read);
    if (line) free (line);
}

void create_address_book(Address_book* address_book)
{
    printf("insert full name   : ");
    getinput(address_book->person_list->full_name);
    printf("insert age         : ");
    getinput(address_book->person_list->age);
    printf("insert phone_number: ");
    getinput(address_book->person_list->phone_number);
}

int main()
{
Address_book* test = (Address_book*) malloc(sizeof(Address_book));
    create_address_book(&test);
    print_person(&test);

return 0;

}

it compiled but when I tried to run the program, it gave result like this : enter image description here

do you know what the problem is in this program?? I am still new to C structs and pointers so I would like to get some help or perhaps some explanations about this problem. Any help would be really appreciated.


Solution

  • I tried out your code as is, and the first hint that things probably were going to be unpredictable were listed in the compiler warnings.

    warning: passing argument 1 of ‘create_address_book’ from incompatible pointer type [-Wincompatible-pointer-types]|
    note: expected ‘Address_book *’ {aka ‘struct address *’} but argument is of type ‘Address_book **’ {aka ‘struct address **’}|
    warning: passing argument 1 of ‘print_person’ from incompatible pointer type [-Wincompatible-pointer-types]|
    note: expected ‘const Person *’ {aka ‘const struct people *’} but argument is of type ‘Address_book **’ {aka ‘struct address **’}|
    ||=== Build finished: 0 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|
    

    And launching the program produced basically the same results as you noted.

    craig@Vera:~/C_Programs/Console/Addresses/bin/Release$ ./Addresses 
    insert full name   : Craig
    insert age         : 67
    insert phone_number: 555-555-5555
    full_name    : ��t�U 
    age          : �U 
    phone_number : �Lt�U 
    

    At issue is that the code was attempting to pass a "pointer to a pointer".

    create_address_book(&test);
    

    So after correcting that, this highlighted the next issue noted in the comments about the address structure having an uninitialized pointer causing a segment fault.

    typedef struct address
    {
        Person *person_list;
    
    } Address_book;
    
    craig@Vera:~/C_Programs/Console/Addresses/bin/Release$ ./Addresses 
    insert full name   : Craig
    Segmentation fault (core dumped)
    

    To come up with simplest solution so that your code will initially work, I refactored the definition of the "Address_book" structure to define an array of 100 "Person" structures (100 was an arbitrary value).

    So, the two refactored blocks of code were the "Address_book" structure and the refinement and correction of the pointer usage in the various function calls within the "main" function.

    typedef struct address
    {
        // Person *person_list;         /* This is uninitialized and will cause issues */
        Person person_list[100];        /* A simple way to allocate the memory that will be needed */
    } Address_book;
    
    int main()
    {
        Address_book* test = malloc(sizeof(Address_book));  /* Removed the qualifier as noted in the comments */
        create_address_book(test);                          /* test is already a pointer so no need for the ampersand in this case */
        //print_person(&test);                              /* The print function needs the pointer of the person varialbe and not the address pointer */
        print_person(test->person_list);                    /* Needed to refer to the pointer reference */
    
        return 0;
    }
    

    This resulted in what looks to be the functionality you were trying to achieve.

    craig@Vera:~/C_Programs/Console/Addresses/bin/Release$ ./Addresses 
    insert full name   : Craig
    insert age         : 67
    insert phone_number: 555-555-5555
    full_name    : Craig 
    age          : 67 
    phone_number : 555-555-5555 
    

    There probably are other areas that could be refined, but review these bits of refactoring to hopefully help you understand pointer usage and memory allocation.