如何解决使用 getchar() 读取两个字符串然后在 C 中打印这些字符串的问题
// Begin
void readTrain(Train_t *train){
printf("Name des Zugs:");
char name[STR];
getlinee(name,STR);
strcpy(train->name,name);
printf("Name des Drivers:");
char namedriver[STR];
getlinee(namedriver,STR);
strcpy(train->driver,namedriver);
}
void getlinee(char *str,long num){
char c;
int i = 0;
while(((c=getchar())!='\n') && (i<num)){
*str = c;
str++;
i++;
}
printf("i is %d\n",i);
*str = '\0';
fflush(stdin);
}
// End
因此,使用 void getlinee(char *str,long num) 函数,我希望用户输入到第一个字符串 char name[STR] 和第二个 char namedriver[STR]。最大字符串大小为 STR(30 个字符),如果我输入的第一个字符串(“Name des Zuges”)超过 30 个字符,它将存储在 name[STR] 中,然后我输入第二个字符串,它将存储在名称驱动程序中,然后打印第一个字符串,我没有从用户输入(输入的前 30 个字符)中获取字符串,而且还“附加”到第二个字符串,我根本不知道为什么......否则如果第一个字符串遵守 30 个字符的限制,则效果很好。 这是我的输出,当第一个字符串的输入大于 30 个字符时,问题出在第 5 行“Zugname”中,为什么我只打印第一个字符串时还有第二个字符串...:
楚格名称:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
我是 30
驱动程序名称:xxxxxxxx
我是 8
祖格名称:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaxxxxxxx
驱动程序名称:xxxxxxxx
解决方法
我认为您的问题是您的 train->name
没有以 '\0'
正确终止,因此当您调用 printf("%s",train->name)
时,该函数会一直读取内存,直到找到 '\0'
.在你的情况下,我猜你的结构看起来像:
struct Train_t {
//...
char name[STR];
char driver[STR];
//...
};
在 getlinee()
函数中,您在最后一个字符之后写入 '\0'
。特别是,如果输入的长度超过 30 个字符,则复制前 30 个字符,然后在第 31 个字符 ('\0'
) 处添加 name[30]
。这是第一次缓冲区溢出。
那么这个 '\0'
实际上写在哪里?好吧,在name[30]
,即使您不应该在那里写字。然后,如果您在执行 strcpy(train->name,name);
时具有上述结构,您实际上将复制一个 31 字节长的字符串:30 个字符到 train->name,并且 '\0'
将溢出到 train->driver[0]
.这是第二次缓冲区溢出。
在此之后,您覆盖 train->driver
缓冲区,使 '\0'
消失,您在内存中的数据基本上如下所示:
train->name = "aaa...aaa" // no '\0' at the end so printf won't stop reading here
train->driver = "xxx\0" // but there
,
我相信你有一个类似的结构:
typedef struct train_s
{
//...
char name[STR];
char driver[STR];
//...
} Train_t;
当您尝试将 '\0'
写入长度超过 STR
(在本例中为 30)的字符串时,您实际上是将 '\0'
写入 name[STR]
,即您没有,因为 name
的最后一个元素长度为 STR
的索引为 STR-1
(在本例中为 29),因此您尝试编写一个 {{1} } 在你的数组之外。
而且,由于此结构中的两个字符串一个接一个地存储,因此您正在将 '\0'
写入 '\0'
,您立即将其覆盖,因此在打印出 driver[0]
时,{{ 1}} 直到到达 name
的末尾才找到 printf
,因此它打印了两者。
解决这个问题应该很容易。
只需更改:
'\0'
到:
driver
或者,我会这样做,将数组大小加 1:
while(((c=getchar())!='\n') && (i<num))
,
您的数组大小有一个逐一错误——您有 STR
个字符的数组,并且您将最多 STR
个字符读入其中,但随后您存储了一个 NUL 终止符,总共需要(最多)STR + 1
个字节。因此,只要您有最大大小的输入,就会跑掉数组的末尾并获得未定义的行为。
将 STR - 1
作为第二个参数传递给 getlinee
以获得最简单的修复。
关键问题
以错误的顺序和一对一进行尺寸测试。 ((c=getchar())!='\n') && (i<num)
--> (i+1<num) && ((c=getchar())!='\n')
。否则空字符就没有空间了。在这里消耗过多字符的错误形式。
getlinee()
应该在第一次使用前声明。提示:启用所有编译器警告以节省时间。
其他
使用 int c;
而不是 char c;
可以很好地将典型的 257 种不同的可能结果与 getchar()
区分开来。
fflush(stdin);
是未定义的行为。更好的代码会在与其他代码的行中消耗多余的字符。
void getlinee(char *str,long num)
使用 size_t num
更好。 size_t
是数组大小和索引的正确大小类型。
int i
应该与 num
的类型相同。
更好的代码也会测试 EOF
。
while((i<num) && ((c=getchar())!='\n') && (c != EOF)){
更好的设计会从 getlinee()
返回一些东西来表示成功并识别问题,比如文件结束没有读取、输入错误、一行太长和参数问题,比如 str == NULL
、{{ 1}}。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。