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

时间:2019-06-18 18:01:05

标签: c newline fgets c-strings null-terminated

以下代码可以工作,但是如果我输入了10个以上的字符(比方说10个),则输出将变成类似这样的内容:

“狗的名字?aaaaaaaaaDog的品种?狗的名字:aaaaaaaaaDog的品种:”

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

#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);
}

2 个答案:

答案 0 :(得分:0)

以下建议的代码:

  1. 干净地编译
  2. 执行所需的功能
  3. 如果狗名太长就会退出
  4. 删除狗名末尾的换行符
  5. 在犬种结束时删除换行符
  6. 不检查犬种长度的有效性
  7. 使用动态内存后正确清理
  8. 避免使用“魔术”数字
  9. 正确分配动态内存并检查/处理任何错误
  10. 使用:“最多8个字符”,以留出结尾换行符和终止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" ) ] = '\0';

    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" ) ] = '\0';

    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 );
}

答案 1 :(得分:0)

字符数组name10元素声明

char name[10];

如果您正在使用以下fgets呼叫

fgets(dog.name, 10, stdin);

输入10个字符'a'之后,fgets调用仅从输入缓冲区读取9个字符,并在数组后面附加终止符零。'\0'

因此,数组将包含字符串"aaaaaaaaa"。与通过以下方式初始化数组相同

char name[10[ = { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', '\0' };

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

fgets(dog.breed, 10, stdin);

结果,数组bread将包含字符串"a\n"

与通过以下方式初始化数组相同

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

如果要在数组中存储更多字符的字符串,则应扩大数组。

例如,如果要为数组名输入一个10个字符'a'的字符串,则可以将数组声明为具有12个元素。为什么12?因为除了10个字符'a'和终止的零字符之外,函数fgets还将尝试从输入缓冲区读取换行符'\n'。否则,将再次调用fgets来读取此字符。

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

#include <string.h>

//...

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