我一直遇到一个非常简单的程序问题而且我真的不知道为什么。有一个人的结构:
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;'扫描字符串时? (我读到它和我理解的方式,没有必要......是吗?)
答案 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);