这是我在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 %dn", i);
*str = ' ';
fflush(stdin);
}
// End
因此,使用void getlinee(char*str,long-num(函数,我希望获得第一个字符串char-name[str]和第二个字符串charnamedriver[str]的用户输入。最大字符串大小是STR(30个字符(,如果我在输入端有超过30个字符的第一个字符串("Name des Zuges"(,它将存储在名称[STR]中,然后我输入第二个字符串,它将被存储在名称驱动程序中,然后打印第一个字符串,我不会从用户输入中获得字符串(来自输入的前30个字符;附";对此,我只是不知道为什么。。。否则,如果第一个字符串满足30个字符的限制,则效果良好。在我的输出中,当第一个字符串的输入大于30个字符时,问题出现在第5行;Zugname";,为什么我只打印第一个字符串时也有第二个字符串…:
祖格人的名字:aaaaaaaaaa
我是30
驱动程序名称:xxxxxxxx
我是8
Zugname:aaaaaaaaaa aaaaaaa xxxxxxxx
驱动程序名称:xxxxxxxx
我认为您的问题是train->name
没有正确地用' '
终止,因此当您调用printf("%s", train->name)
时,函数会一直读取内存,直到找到' '
。在你的情况下,我想你的结构看起来像:
struct Train_t {
//...
char name[STR];
char driver[STR];
//...
};
在getlinee()
函数中,在最后一个字符之后写入' '
。特别是,如果输入长度超过30个字符,则复制前30个字符并在第31个字符(name[30]
(处添加' '
。这是第一个缓冲区溢出。
那么这个' '
到底写在哪里呢?好吧,在name[30]
,尽管你不应该在那里写作。然后,如果您在执行strcpy(train->name, name);
时具有上述结构,那么您实际上会将一个31字节长的字符串:30个字符复制到train->名称,并且' '
将溢出到train->driver[0]
中。这是第二次缓冲区溢出。
之后,您覆盖train->driver
缓冲区,这样' '
就会消失,您在内存中的数据基本上看起来像:
train->name = "aaa...aaa" // no ' ' at the end so printf won't stop reading here
train->driver = "xxx " // but there
您的数组大小有一个逐个错误——您有STR
个字符的数组,您可以向其中读取多达STR
个字符,但随后您存储了一个NUL终止符,总共需要(多达(STR + 1
个字节。因此,每当您有一个最大大小的输入时,您就会运行数组的末尾,并得到未定义的行为。
将STR - 1
作为第二个参数传递给getlinee
以获得最简单的修复。
关键问题
尺寸测试顺序错误,逐个关闭。((c=getchar())!='n') && (i<num)
->CCD_ 22。否则就没有空间容纳空字符。在此处消耗多余字符的形式不正确。
getlinee()
应在首次使用前声明。提示:启用所有编译器警告以节省时间。
其他
使用int c;
而不是char c;
来很好地区分典型的257个不同的可能结果与getchar()
。
fflush(stdin);
是未定义的行为。更好的代码会与其他代码一起在一行中消耗多余的字符。
CCD_ 28优于CCD_ 29。size_t
是用于调整数组大小和索引的正确大小类型。
int i
的类型应与num
的类型相同。
更好的代码也可以测试EOF
。
while((i<num) && ((c=getchar())!='n') && (c != EOF)){
一个更好的设计是从getlinee()
返回一些东西来指示成功,并识别问题,如文件结尾没有读取、输入错误、行太长以及参数问题,如str == NULL
、num <= 0
。
我相信你有一个类似的结构:
typedef struct train_s
{
//...
char name[STR];
char driver[STR];
//...
} Train_t;
当您尝试将' '
写入比STR
(本例中为30(长的字符串时,实际上您将' '
写入name[STR]
,但您没有,因为name
中最后一个长度为STR
的元素的索引为STR-1
(本例为29(,所以您正尝试在数组外写入' '
。
而且,由于该结构中的两个字符串是相继存储的,因此您正在将' '
写入driver[0]
,并立即覆盖它,因此当打印出name
时,printf
直到到达driver
的末尾才找到' '
,因此它打印这两个字符串。
解决这个问题应该很容易。
只需更改:
while(((c=getchar())!='n') && (i<num))
至:
while(((c=getchar())!='n') && (i<num - 1))
或者,正如我所做的,在数组大小上加1:
char name[STR + 1];
char driver[STR + 1];