为什么多个 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);
}
以下建议代码:
- 干净地编译
- 执行所需的功能
- 如果狗名太长则退出
- 删除狗名末尾的换行符
- 删除狗品种末尾的换行符
- 不检查狗品种长度的有效性
- 使用动态内存后正确清理
- 避免使用 'magic' 数字
- 正确分配动态内存和checks/handles任何错误
- 使用:'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=]';
下面的代码可以工作,但是如果我输入超过 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);
}
以下建议代码:
- 干净地编译
- 执行所需的功能
- 如果狗名太长则退出
- 删除狗名末尾的换行符
- 删除狗品种末尾的换行符
- 不检查狗品种长度的有效性
- 使用动态内存后正确清理
- 避免使用 'magic' 数字
- 正确分配动态内存和checks/handles任何错误
- 使用:'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=]';