scanf string - c语言

时间:2015-12-09 04:13:51

标签: c scanf

下面是我用来读取和打印struct数组值的代码 - 我在读取字符串时遇到错误:

#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");
        }
    }
}

这是一个基本程序 - 但它会因未知错误而关闭。 enter image description here

如果字符串输入的长度只有3个字符,那么就没有错误。 如果我出错了,请你纠正我。

尝试下面的代码但是没有工作

printf("Enter First Name: ");
                scanf(" %s", &addr[add].fname);
                printf("Enter last Name: ");
                scanf(" %s", &addr[add].lname);
                printf("Enter Contact Number: ");
                scanf(" %s", &addr[add].num);
                printf("Enter Email: ");
                scanf(" %s", &addr[add].email);
                add=add+1;

1 个答案:

答案 0 :(得分:2)

假设这是一个32位系统,因此每个指针都是4个字节。你的addressbook结构在内存中看起来像这样:

    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个额外的字节内存,这会占用堆栈中的其他内容。现在可能只是发生成为addm变量,现在这些变量具有巨大的价值。然后你写信给addr[add]和BOOM - 你只是在堆栈之外的某处写入内存。

这是一个场景。真的,任何事情都可能发生。关键是这是未定义的行为,你应该不惜一切代价避免这种行为!

怎么办?好吧,到处都有关于此的大型主题,以及为什么scanf BAD 首先读取字符串。但在紧要关头,我们假设您的用户表现得很好。如果您只是将这些指针更改为数组,生活将会改善:

/* 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);

当然,这不是唯一的解决方案。它已经不安全了。但是我不想在这一点上用太多的信息来压倒你。希望这是一个有用的开始。小心,好吗?! =)