在C语言中使用malloc函数和结构体

4
所以我正在尝试将malloc添加到我创建的电话簿应用程序中,但由于我对C语言还比较陌生,所以不确定我所做的是否正确。我遇到了一个小问题,但是我已经阅读了我拥有的初学者书籍,它没有提供足够详细的信息,我无法通过搜索Google来确定我是否在设置malloc时完全错误,或者是否有其他我错过的东西。
基本上,我的结构中有4个数组,分别是“First_Name”,“Last_name”,“home”和“cell”。每个数组都有两个函数,一个从用户获取信息并将用户信息打印并添加到电话簿中的函数。现在我只有一小部分原始代码,仅将名字添加到电话簿中(因此不是整个代码),并且在获取用户输入的每个函数中,我想添加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);
        }
}

希望能够得到帮助的人,我想给予他们一个大大的绿色勾号。


1
setFirst 函数中,你正在释放你的 friends 缓冲区,实际上是在说我不再需要它了。当你这样做时,那个内存就会消失。如果你要为调用者动态分配结构体,你要么提供一个单独的释放函数,要么让用户知道他们有责任清理那个结构体。此外,你只是改变了本地副本的 friends 指针。如果你想将调用者的指针指向一个新的缓冲区,你需要将参数类型更改为 fr ** - jpm
1
@jpm:这是一个答案!发布它! - Aubin
1
执行 scanf("%s",invitingBufferOverflow); 和使用 gets(invitingBufferOverflow) 一样糟糕(或更糟)。 - Happy Green Kid Naps
4个回答

8
您需要为整个记录以及每个字段分别分配内存。例如:
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);
}

...

1
+1 对于管理函数。这绝对比在客户端函数中强行分配/释放逻辑要好。 - jpm

1

这里有一些问题:

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

现在你正在使用malloc分配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

你刚刚失去了所有的朋友。:)
你确实需要释放内存,但应该在程序结束或者你使用完之后再释放。如果你一边分配内存一边立即释放它,那么内存就会被清空,你将无法再访问它。


嗯,我想我要做的是为每个变量分配内存,而不是静态地分配。我猜每次输入用户输入时使用malloc并不能做到这一点?作为一个新手,生活真难啊。 - Jcmoney1010

0

你的结构体只包含指针,没有分配内存。最好定义它来容纳数组,然后将名称等写入其中:

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;

这是我原来电话簿应用程序中的内容,但在阅读了有关malloc的资料后,我认为我可以将所有这些内容转换为char指针,然后在malloc函数中使用缓冲区。 - Jcmoney1010
这不是 OP 所要求的。这只是一种避免问题而不是解决问题的方法。 - Aubin
我认为在原始帖子中可能误传了我的意图。当用户输入朋友的信息时,我希望指针分配适当的空间,并将朋友信息存储在这个分配的空间中。我已经阅读了其他地方提到使用缓冲数组作为scanf参数的片段,但我仍然有些困惑如何将所有这些内容组合在一起。 - Jcmoney1010

0

这里有几个需要考虑的问题,但首先请考虑以下内容。

setFirst 中,你正在释放你的 friends 缓冲区,本质上是在说“我不再需要这个了。” 当你这样做时,那个内存就消失了。如果你要为调用者动态分配结构体,你要么提供一个单独的释放函数,要么让用户知道清理该结构体是他们的责任。

此外,你只在本地副本中更改了 friends 指针。如果你想将调用者的指针指向一个新缓冲区,你需要将参数类型更改为 fr**


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接