微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 getchar() 读取两个字符串然后在 C 中打印这些字符串的问题

如何解决使用 getchar() 读取两个字符串然后在 C 中打印这些字符串的问题

这是我用 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 举报,一经查实,本站将立刻删除。