连续scanf正在添加字符串

时间:2016-11-09 17:26:36

标签: c string struct scanf

我一直遇到一个非常简单的程序问题而且我真的不知道为什么。有一个人的结构:

typedef struct {
   char name[50];
   char p_id[11];
   char cel[11];
   int by;
   int id;
} Person;

现在,还有另一个结构代表联系人列表:

typedef struct {
   Person * people;
} lContacts;

我一直在尝试将此人的数据包含在其中,并将该人员添加到联系人列表中。这个人正常添加,所以我不会在这里发布代码,但是当我读到字符串时出现了错误:

void include(lContacts * myContacts)
{

    Person p;

    scanf("%s", p.name);
    scanf("%d", &p.by); //birth year
    scanf("%s", p.p_id);
    printf("TEST P_ID: %s\n\n", p.p_id);
    scanf("%s", p.cel);
    printf("TEST P_ID AGAIN: %s\n\n", p.p_id);

    myContacts->people[index]=p; //don't worry about the index, there is a piece of code I'm omitting to make it easier to read, just assume it is right.
    }
}

请注意,我在那里打印测试,因为当我列出我的联系人时,联系人p_id自己与cel连接在一起,所以我打印了整个代码,直到我发现错误在那里。

以下是输入示例:

Name
1991
11111111111
<console prints| TEST P_ID: 11111111111>
22222222222
<console prints| TEST P_ID AGAIN: 1111111111122222222222>

但是,如果我打印p.cel,则会正确打印

<console prints 22222222222>

有什么想法吗?也许我应该使用'&amp;'扫描字符串时? (我读到它和我理解的方式,没有必要......是吗?)

2 个答案:

答案 0 :(得分:1)

这实际上是一个非常微小的错误。基本上,您在字符串p.p_id中遇到缓冲区溢出

当您输入p.p_id的值11111111111(11次1)时,您必须意识到p_id字符串的实际容量被声明为{ {1}}必须在末尾包含NULL字符。

如果将11分配给11111111111,则该字符串中的NULL字符将没有剩余空间,因此其末尾不会有任何空格。

当您输入结构的下一个成员字符串的值时,同样会发生这种情况。

现在,当您尝试打印p_id的值时,将打印该值,直到在字符串中找到p.p_id(NULL字符)。但是,这里没有,所以下一个字符串将开始打印! (缓冲区或字符串'\0'溢出)

这是由于在C中的结构中完成的成员对齐而发生的。下一个字符串将存储在连续的存储单元中,因此打印将继续。 (如果有第三个连续的字符串成员,那么由于第二个字符串中的缓冲区溢出,它也会打印出来!)

但是,打印在第二个字符串之后停止,因为由于某些结构对齐而在下一个连续的存储单元中没有数据,或者在下一个单元格中可能存在实际的p_id值,该值被解释为NULL字符

要避免使用较大的字符数组或使用动态分配的字符串。

答案 1 :(得分:0)

正如@skrtbhtngr指出的那样,你有一个缓冲区溢出。为了防止以后出现这种情况,您应该在未知字符串输入上使用fgets而不是scanf。如果输入已损坏,此示例将exit(1)

int getsafestring(char* s, int maxlen)
{
   if(fgets(s,maxlen,stdin) == NULL) return 1; // read error
   if(strlen(s) == 0) return 2; // other read error
   if(s[strlen(s)-1] !='\n') return 3; // buffer overflow error
   s[strlen(s)-1]=0; // replace newline with null
   return 0;
}

if(Getsafestring(p.name,sizeof(p.name))) exit(1);
if(scanf("%d", &p.by) != 1)              exit(1);
if(getsafestring(p.p_id,sizeof(p.p_id))) exit(1);
if(getsafestring(p.cel,sizeof(p.cel)))   exit(1);