如何为长度未知的输入字符串分配内存?

3
这是结构体:

struct:

typedef struct _friend {
        char *firstname;
        char *lastname;
        char birthdate[9];
} friend;

我对如何让用户输入一个字符串并将其放入friend结构的firstname(或lastname)中感到困惑。此外,如果用户在使用fgets时输入多于256个字符怎么办?目前我有以下内容...

friend *f = (friend *)malloc(sizeof(friend));  //initialize f pointer to friend
char *str;

fgets(str,256,stdin);
f->firstname = (char*)malloc(sizeof(char)*(strlen(str)+1));
strcpy(f->firstname,str);

1
在C程序中,您不需要转换malloc()的返回值。 - Carl Norum
3
您没有为str分配内存。 - Musa
如果我不知道用户输入的字符串有多长,我该如何为其分配内存?@Musa - Michael_19
1
设置一个合理的限制,256比我遇到的任何名称都要长得多。 str = malloc(sizeof(char)*256); - Musa
3个回答

2

由于 stdin 是带缓冲的输入,因此您可以使用 fgetc 按字符读取输入,直到遇到换行符或 EOF。也许您正在寻找类似以下代码的内容:

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

struct friend {
        char    *firstname;
        char    *lastname;
        char     birthdate[9];
};

static char *read_line(FILE *f)
{
    char    *r = NULL;
    char    *p = r;
    char    *e = p;
    int  c;

    while ((c = fgetc(f)) != EOF) {
        if (p >= e) {
            size_t   l = e > r ? (size_t)(e - r) : 32u;
            char    *x = realloc(r, l);
            if (!x) {
                free(r);
                r = NULL;
                goto out;
            }
            p = x + (p - r);
            e = x + l;
            r = x;
        }
        if (c != '\n') {
            *p++ = (char)c;
        } else {
            *p++ = '\0';
            goto out;
        }
    }
    if (ferror(f) != 0) {
        free(r);
        r = NULL;
    }
out:
    return r;
}

int main(void)
{
    struct friend f;

    memset(&f, 0, sizeof(struct friend));

    printf("Please enter your first name: ");
    fflush(stdout);
    f.firstname = read_line(stdin);
    if (!f.firstname)
        goto on_error;
    printf("Please enter your last name: ");
    fflush(stdout);
    f.lastname = read_line(stdin);
    if (!f.lastname)
        goto on_error;

    printf("You first name is: %s\n", f.firstname);
    printf("Your last name is: %s\n", f.lastname);

    free(f.firstname);
    free(f.lastname);
    return EXIT_SUCCESS;

on_error:
    perror("read_line");
    free(f.firstname);
    free(f.lastname);
    return EXIT_FAILURE;
}

2
这个问题没有一种通用的解决方案。不同的工程师可能会根据各种标准使用几种解决方案来解决相同的问题:
  • 解决方案是否应该非常简单易懂?
  • 是否应该灵活适应各种输入长度?
  • 代码是否仅在已知具备足够内存的有限环境中运行?
  • 是否还需要其他人理解解决方案,例如进行持续维护?

对于已知内存充足并且简单的解决方案,我可能会这样做:

char  buf [1000];  // for English names, this should be big enough
friend f;  // not a pointer, but a full struct
if (!fgets (buf, sizeof buf, stdin))
{
      perror ("error reading from stdin");
      return;
}
f.firstname = strdup (buf);  // allocates the right size of memory and copies

...
// when finished with the structure, deallocate the dynamic strings:
free (f.firstname);

注意这个代码几乎完全避免了操作指针?(只有strdup()在做,而且它巧妙地封装了必要的操作。)这是强健、低麻烦代码的特点。


1

不要这样做。

为用户输入设置一个单独的缓冲区。然后,在用户将数据输入到该缓冲区后,解析它并确定它是否合理(例如,如果它不是空字符串,并且不包含任何数字或其他奇怪的字符),并去除多余的空格等。如果数据可接受,则确定其实际长度,并分配正确数量的内存来复制它。

为了将用户输入放入单独的缓冲区中,请勿使用 fgets()。而是在循环中使用 fgetc(),以便在必要时可以增加缓冲区的大小。例如,您可以从一个小的32字节缓冲区开始,然后在缓冲区满时使用 realloc() 来将缓冲区的大小加倍。


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