scanfstring-c语言
下面是我用来读取和打印结构数组值的代码-读取字符串时出错:scanfstring-c语言,c,scanf,C,Scanf,下面是我用来读取和打印结构数组值的代码-读取字符串时出错: #include <stdio.h> #include <stdlib.h> struct addressbook{ char *fname; char *lname; char *num; char *email; }; int main() { struct addressbook addr[100]; int add=0,m=0; while (m
#include <stdio.h>
#include <stdlib.h>
struct addressbook{
char *fname;
char *lname;
char *num;
char *email;
};
int main() {
struct addressbook addr[100];
int add=0,m=0;
while (m<3)
{
printf("1. Show All Entries\n");
printf("2. Add Entry\n");
printf("3. Quit\n");
scanf("%d",&m);
if (m==1)
{
int i;
for (i=0; i<add;i++)
{
printf("FName: %s , LName: %s , Number: %s , Email: %s \n",&addr[i].fname, &addr[i].lname,&addr[i].num,&addr[i].email);
}
}
else if (m==2)
{
if (add<101)
{
struct addressbook a;
printf("Enter First Name: ");
scanf(" %s", &a.fname);
printf("Enter last Name: ");
scanf(" %s", &a.lname);
printf("Enter Contact Number: ");
scanf(" %s", &a.num);
printf("Enter Email: ");
scanf(" %s", &a.email);
addr[add] = a;
add=add+1;
}
else{printf("100 limit reached");}
}
else if (m=3)
{
m=3;
}
else
{
m=0;
printf("Invalid option");
}
}
}
让我们假设这是一个32位系统,因此指针每个是4个字节。您的
地址簿
结构在内存中如下所示:
0 1 2 3 4 5 6 7 8 9 A B C D E F
---------------------------------
[fname ][lname ][num ][email ]
当您声明局部变量struct addressbook a代码>,这正是堆栈上得到的--16字节未初始化内存:
0 1 2 3 4 5 6 7 8 9 A B C D E F
---------------------------------
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
现在,您开始使用scanf
读取实际指针值。以下是第一次输入后结构的外观:
0 1 2 3 4 5 6 7 8 9 A B C D E F
----------------------------------
a s d a s d a \0 ? ? ? ? ? ? ? ?
在最后一次输入之后:
0 1 2 3 4 5 6 7 8 9 A B C D E F * * * * * *
---------------------------------------------
a s d a c a s c 1 2 3 1 a s d a s d a s d \0
哎呀!因此,您从结构的末尾额外写入了6个字节的内存,这会消耗堆栈中的任何其他内容。现在可能正好是add
和m
变量,它们现在是一些巨大的值。然后你写到addr[add]
和BOOM——你只是在堆栈之外的某个地方写到内存中
这是一种情况。任何事情都有可能发生,真的。关键是这是未定义的行为,您应该不惜一切代价避免这种行为
怎么办?关于这一点,到处都有很多话题,为什么scanf
首先不利于读取字符串。但在紧要关头,让我们假设您的用户表现良好。如果您只是将这些指针更改为数组,则生活将得到改善:
/* Arbitrary string length limits */
#define MAX_FNAME 20
#define MAX_LNAME 20
#define MAX_NUM 20
#define MAX_EMAIL 50
struct addressbook{
char fname[MAX_FNAME];
char lname[MAX_LNAME];
char num[MAX_NUM];
char email[MAX_EMAIL];
};
现在,在调用scanf
时,请确保使用了正确的指针。如果使用a.fname
,编译器会将数组分解为指针,因此不应传递&a.fname
。您可以编写a.fname
或&a.fname[0]
:
scanf(" %s", a.fname);
当然,这不是唯一的解决方案。而且已经不安全了。但我不想在这一点上用太多的信息压倒你。希望这是一个有益的开始。小心点,好吗 scanf(“%s”和a.fname);没有正确初始化任何结构字段。事实上,它们根本没有草签。因此,您不能在scanf
中使用它们scanf
不会为您分配内存。您需要为其提供有效的缓冲区进行写入。这是未定义的行为。您没有为这些指针分配内存。更糟糕的是,你实际上并不是在向它们指向的内存中写入字符串,而是在向实际指针中写入字符串,这本身与错误无关,你可能需要注意这样的错误:,否则如果(m=3)
-这实际上是一个始终为真的赋值。我将m=3修改为m==3-但其余部分不清楚,我试着从我的角度来解决这个问题,我已经习惯了VB.net,但c似乎太难了:(这是一个很好的解释。需要学习很多东西-VB比这简单得多:(不用担心,VB可能更简单,但是没有人用VB实现操作系统、设备驱动程序或严肃编译器。习惯于在C中小心。开始思考程序如何在CPU上执行任务。如果你想要一种更具表现力和同样强大的语言,你可以学习C++。但是现在,慢慢来。熟悉C语言的这种新的神奇功能吧。你很快就会意识到它踢了VB的屁股。
scanf(" %s", a.fname);