Использование malloc со структурами в c

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

В основном у меня есть 4 массива в моей структуре: First_Name, Last_name, home, cell. У каждого из них есть 2 функции: функция, которая получает информацию от пользователя, и функция, которая распечатывает и добавляет информацию о пользователе в телефонную книгу. Сейчас у меня есть небольшой фрагмент исходного кода, который добавляет только первое имя в телефонную книгу (так что это не весь код), и в каждую функцию, которая получает пользовательский ввод, я хочу добавить функцию malloc. Прямо сейчас у меня есть только первое имя и первая настройка malloc, но проблема заключается в том, что когда я иду проверять телефонную книгу, чтобы убедиться, что имя было введено успешно, программа завершает работу. Если я удалю malloc, он успешно работает.

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

#define BUFFER 50
    //Structure for contacts
typedef struct friends_contact {

    char *First_Name;
    char *Last_Name;
    char *home;
    char *cell;
} fr;

void menu(fr * friends, int *counter, int user_entry, int i);
void setFirst(fr *, int *, int i);
char getFirst(fr *, int i);
void add_contact(fr * friends, int *counter, int i);
void print_contact(fr * friends, int *counter, int i);

int main()
{

    int user_entry = 0;
    fr *friends;
    int counter = 0;
    int i = 0;
    menu(friends, &counter, user_entry, i);
    getch();
    return 0;
}

//Menu function
void menu(fr * friends, int *counter, int user_entry, int i)
{
    do {
        int result;

        printf("\nPhone Book Application\n");
        printf
            ("1) Add friend\n2) Delete friend\n3) Show a friend\n4)Showphonebook\n5)Exit\n");
        scanf("%d", &user_entry);

        if (user_entry == 1) {
            add_contact(friends, counter, i);
        }
        if (user_entry == 2) {

        }
        if (user_entry == 3) {

        }
        if (user_entry == 4) {
            print_contact(friends, counter, i);
        }
    } while (user_entry != 5);
}

void setFirst(fr * friends, int *counter, int i)
{
    // THE MALLOC FUNCTION!
    friends = (fr *) malloc(BUFFER * sizeof(fr));
    printf("Enter a first name \n");
    scanf("%s", friends[*counter].First_Name);
    if (friends != NULL) {

        free(friends);
    }
}

char getFirst(fr * friends, int pos)
{
    printf("%s ", friends[pos].First_Name);
    return *friends[pos].First_Name;
}

void add_contact(fr * friends, int *counter, int i)
{
    setFirst(friends, counter, i);
    (*counter)++;
}

void print_contact(fr * friends, int *counter, int i)
{
    for (i = 0; i < *counter; i++)
        if (strlen(friends[i].First_Name)) {
            getFirst(friends, i);
        }
}

Хочу поставить большую зеленую галочку тому, кто может мне помочь.


person Jcmoney1010    schedule 09.11.2012    source источник
comment
В setFirst вы free заполняете свой friends буфер, по сути говоря, что мне это больше не нужно. Когда вы делаете это, эта память просто уходит. Если вы собираетесь динамически выделять структуры для вызывающей стороны, вы должны либо предоставить отдельную функцию освобождения, либо сообщить пользователю, что он несет ответственность за очистку этой структуры. Кроме того, вы всегда меняете только локальную копию указателя друзей. Если вы хотите указать указатель вызывающего объекта на новый буфер, вам нужно изменить тип аргумента на fr**.   -  person jpm    schedule 09.11.2012
comment
@jpm: это ответ! Разместите это!   -  person Aubin    schedule 09.11.2012
comment
Делать scanf("%s",invitingBufferOverflow); так же ужасно (или еще хуже), как gets(invitingBufferOverflow)   -  person Happy Green Kid Naps    schedule 09.11.2012


Ответы (4)


Вам нужно выделить память как для записи в целом, так и отдельно для каждого поля. Например:

void string_realloc_and_copy (char **dest, const char *src)
{
  size_t len = strlen (src);
  *dest = realloc (*dest, len + 1);
  memcpy (*dest, src, len + 1);
}

typedef struct
{
  char *name;
  char *title;
} record;

record * record_new ()
{
  record *r = malloc (sizeof (record));
  r->name = NULL;
  r->title = NULL;
  return r;
}

void record_free (record *r)
{
  free (r->name);
  free (r->title);
  free (r);
}

void record_set_name (record *r, const char *name)
{
  string_realloc_and_copy (&r->name, name);
}

void record_set_title (record *r, const char *title)
{
  string_realloc_and_copy (&r->title, title);
}

Теперь, чтобы создать запись и заполнить ее значениями, прочитанными от пользователя:

record *r;
char buffer[100 + 1];

r = record_new ();

printf("Enter a first name \n");
if (scanf ("%100s", buffer) == 1) {
  record_set_name (r, buffer);
}

...
person Community    schedule 09.11.2012
comment
+1 за функции управления. Это определенно предпочтительнее, чем логика alloc/dealloc, разбросанная по клиентским функциям. - person jpm; 09.11.2012

Есть некоторые проблемы здесь:

void setFirst(fr*friends, int* counter, int i) {
   // THE MALLOC FUNCTION!
   friends=(fr*) malloc(BUFFER*sizeof(fr));  <-- This is not doing what you're thinking

sizeof(fr) будет размером, необходимым для 4 указателей на символ. Например, если вы работаете на 32-битной платформе x86, для указателя на char требуется 4 байта, таким образом:

sizeof(fr) == 4 x 4 == 16 bytes

Итак, теперь вы выделяете 16*BUFFER или 16x50 = 800 байт. Это позволяет вам иметь массив из 50 структур 'fr'.

fr * friend
        |
        +--------> FirstName*
            |      LastName*
            |      home*
            |      cell*
            +----> FirstName*
            |       LastName*
            |      home*
            |      cell*
            ...

Итак, у вас есть память для 50 структур, но содержимое этих структур все еще не имеет памяти. Вам нужно выделить память для каждого члена структуры (и не забудьте также освободить их всех), или вы можете сделать их статическими членами с массивами вместо указателей.

Вторая проблема:

if(friends != NULL)  <-- if malloc was successful
{
     free(friends);  <-- release the memory

Ты только что потерял всех своих друзей. :)
Вам нужно освободить память, но в конце программы или в конце того места, где вы ее используете. Если вы назначите, а затем сразу же освободите, то память исчезнет, ​​и вы больше не сможете получить к ней доступ.

person Mike    schedule 09.11.2012
comment
хммм.. Я думаю, что я пытаюсь выделить память каждому из них, не делая это статически. Я предполагаю, что использование malloc каждый раз, когда я ввожу пользовательский ввод, не делает этого? тяжело быть нубом, лол. - person Jcmoney1010; 10.11.2012

Здесь есть еще несколько вещей, которые следует учитывать, но для начала рассмотрим следующее.

В setFirst вы free обрабатываете свой friends буфер, по сути говоря: "Мне это больше не нужно". Когда вы делаете это, эта память просто уходит. Если вы собираетесь динамически выделять структуры для вызывающей стороны, вы должны либо предоставить отдельную функцию освобождения, либо сообщить пользователю, что он несет ответственность за очистку этой структуры.

Кроме того, вы всегда меняете только локальную копию указателя друзей. Если вы хотите указать указатель вызывающего объекта на новый буфер, вам нужно изменить тип аргумента на fr**.

person jpm    schedule 09.11.2012

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

typedef struct friends_contact{

    char First_Name[20];
    char Last_Name[20];
    char home[20];
    char cell[20];
} fr;

Здесь я сделал каждое поле длиной 20 символов, но вы можете изменить это по своему усмотрению.


Редактировать: да, конечно, вы можете использовать динамическую память, но стоит ли заморачиваться? Преимущество динамических строк в том, что они могут быть точно нужного размера; вы можете сэкономить несколько байтов, и вы гарантируете, что сможете вписать имена в поля. Но много ли имен длиннее 20 символов и имеет ли смысл сокращать некоторые из них? С malloc есть много неудобных распределений (каждое из которых может дать сбой) и, конечно же, освобождения.

В качестве компромисса можно сделать телефонные номера фиксированного размера (они не меняются), а имена динамическими; затем распределите имена с помощью strdup (что также может привести к сбою).

typedef struct friends_contact{

    char *First_Name;
    char *Last_Name;
    char home[12];
    char cell[12];
} fr;
person William Morris    schedule 09.11.2012
comment
Это то, что у меня было в моем исходном приложении телефонной книги, но, прочитав о malloc, я подумал, что смогу превратить все это в указатели на символы, а затем использовать буфер в функции malloc. - person Jcmoney1010; 09.11.2012
comment
Это не то, о чем просит ОП. Это способ избежать проблем, а не решать их. - person Aubin; 09.11.2012
comment
Я думаю, что, возможно, я исказил то, что хотел сделать в исходном посте. Когда пользователь вводит информацию о друге, я хочу, чтобы указатель выделял соответствующее пространство, а информация о друзьях хранилась в этом выделенном пространстве. я читал фрагменты других мест, где упоминается использование буферного массива в качестве аргумента для scanf, но я просто собираю все это вместе - person Jcmoney1010; 10.11.2012