为什么多个 fgets 语句会覆盖字符数组?

Why does multiple fgets statements overwrite character arrays?

下面的代码可以工作,但是如果我输入超过 10 个字符(比如 10 个 a),输出就会变成这样:

"Dog's name? aaaaaaaaaDog's breed?Dog's name: aaaaaaaaaDog's breed:"

这是为什么?我该如何解决?

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

typedef struct Dog {
    char name[10];
    char breed[10];
} Dog;

Dog makeDog() {
    Dog dog;

    printf("Dog's name? ");
    fgets(dog.name, 10, stdin);

    printf("Dog's breed? ");
    fgets(dog.breed, 10, stdin);

    return dog;
}

int main() {
    printf("\n");

    Dog dog = makeDog();

    printf("\n");

    printf("Dog's name: %s", dog.name);
    printf("Dog's breed: %s \n", dog.breed);
}

以下建议代码:

  1. 干净地编译
  2. 执行所需的功能
  3. 如果狗名太长则退出
  4. 删除狗名末尾的换行符
  5. 删除狗品种末尾的换行符
  6. 不检查狗品种长度的有效性
  7. 使用动态内存后正确清理
  8. 避免使用 'magic' 数字
  9. 正确分配动态内存和checks/handles任何错误
  10. 使用:'max 8 characters' 为尾随换行符和终止 NUL 字符留出空间

现在,建议的代码:

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

#define MAX_NAME_LEN  10
#define MAX_BREED_LEN 10

typedef struct Dog 
{
    char name[ MAX_NAME_LEN ];
    char breed[ MAX_BREED_LEN ];
} Dog;

Dog * makeDog() 
{
    Dog *dog = malloc( sizeof( Dog ) );
    if( !dog )
    {
        perror( "malloc for struct Dog failed" );
        exit( EXIT_FAILURE );
    }

    printf("Dog's name? max 8 characters ");
    if( !fgets(dog->name, MAX_NAME_LEN, stdin) )
    {
        perror( "fgets for dog name failed" );
        exit( EXIT_FAILURE );
    }

    if( dog->name[ strlen( dog->name ) -1 ] != '\n' )
    {
        puts( "dog name too long, aborting" );
        exit( EXIT_FAILURE );
    }

    // remove trailing newline
    dog->name[ strcspn( dog->name, "\n" ) ] = '[=10=]';

    printf("Dog's breed? max 8 characters");
    if( !fgets(dog->breed, MAX_BREED_LEN, stdin) )
    {
        perror( "fgets for dog breed failed" );
        exit( EXIT_FAILURE );
    }

    // remove trailing newline
    dog->breed[ strcspn( dog->name, "\n" ) ] = '[=10=]';

    return dog;
}

int main() {
    printf("\n");

    Dog *dog = makeDog();

    printf("\n");

    printf("Dog's name: %s\n", dog->name);
    printf("Dog's breed: %s\n", dog->breed);

    free( dog );
}

字符数组 name 声明为 10 个元素

char name[10];

如果您正在使用 fgets

的以下调用
fgets(dog.name, 10, stdin);

输入 10 个字符 'a' 之后,fgets 调用仅从输入缓冲区读取 9 个字符,并在数组中附加终止字符 '[=22=]'

因此数组将包含字符串 "aaaaaaaaa"。和下面这样初始化数组是一样的

char name[10[ = { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', '[=12=]' };

之后输入缓冲区将包含一个字符'a'和换行符'\n'。这些字符将在下次调用时读取

fgets(dog.breed, 10, stdin);

因此数组 bread 将包含字符串 "a\n".

和下面初始化数组一样

char bread[10[ = { 'a', '\n', '[=14=]' };

如果你想在数组中存储更多字符的字符串,你应该扩大数组。

例如,如果您想为数组名称输入一个 10 个字符 'a' 的字符串,您必须将数组声明为具有 12 个元素。为什么 12?因为除了 10 字符 'a' 和终止零字符之外,函数 fgets 还将尝试从输入缓冲区读取换行符 '\n' 。否则这个字符将被第二次调用 fgets.

读取

要从数组中删除换行符,您可以使用以下方法

#include <string.h>

//...

fgets( dog.name, 12, stdin );
dog.name[strcspn( dog.name, "\n" )] = '[=15=]';