请教一个scanf和scanf_s导致的区别

diabolosis 2018-03-16 11:54:13
环境:VC++2010学习版
题目:3. 编程:定义5个变量,通过键盘将“10, -10, 8.123678309, a, 3.14”这五个值分别赋给相应变量,然后在屏幕上输出变量的值(每行输出一个)。

代码:

#include<stdio.h>

int main(void)
{
int a,b;
float e;
double c;
char d;

printf ("请输入预定的五个变量,中间以逗号隔开:");
scanf_s ("%d,%d,%lf,%c,%f",&a,&b,&c,&d,&e);

printf(" %d\n %d\n %lf\n %d\n %f",a,b,c,d,e);

getchar();
getchar();
return 0;

}

运行报错:
003.exe 中的 0x52f2f52c (msvcr100d.dll) 处有未经处理的异常: 0xC0000005: 写入位置 0x00000000 时发生访问冲突

在红色高亮的这行如果使用scanf就不会有问题。
原因是什么?
...全文
1280 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2018-03-30
  • 打赏
  • 举报
回复
说scanf_s比scanf安全 就和 五十步笑百步 一样一样的。
热血打工人 2018-03-29
  • 打赏
  • 举报
回复
MSDN中例子: // crt_scanf_s.c // This program uses the scanf_s and wscanf_s functions // to read formatted input. #include <stdio.h> int main( void ) { int i, result; float fp; char c, s[81]; wchar_t wc, ws[81]; result = scanf_s( "%d %f %c %C %s %S", &i, &fp, &c, 1, &wc, 1, s, 81, ws, 81 ); printf( "The number of fields input is %d\n", result ); printf( "The contents are: %d %f %c %C %s %S\n", i, fp, c, wc, s, ws); result = wscanf_s( L"%d %f %hc %lc %S %ls", &i, &fp, &c, 2, &wc, 1, s, 81, ws, 81 ); wprintf( L"The number of fields input is %d\n", result ); wprintf( L"The contents are: %d %f %C %c %hs %s\n", i, fp, c, wc, s, ws); return 0; }
bluestar2009 2018-03-21
  • 打赏
  • 举报
回复
scanf()在读取数据时不检查边界,所以可能会造成内存访问越界: 1 2 3 4 //例如:分配了5字节的空间但是用户输入了10字节,就会导致scanf()读到10个字节 char buf[5]={'\0'}; scanf("%s", buf); //如果输入1234567890,则5以后的部分会被写到别的变量所在的空间上去,从而可能会导致程序运行异常。 以上代码如果用scanf_s()则可避免此问题: 1 2 3 char buf[5]={'\0'}; scanf_s("%s",buf,5); //最多读取4个字符,因为buf[4]要放'\0' //如果输入1234567890,则buf只会接受前4个字符 先看用法 再看你输入的格式
赵4老师 2018-03-16
  • 打赏
  • 举报
回复
printf里面的%和变量的一一对应关系 scanf里面的%和变量以及变量前加不加&的一一对应关系 是C代码中非常容易出错的地方,而且通常编译还不出错。 所以在编译源代码之前值得专门仔细检查一遍甚至多遍。 在每个最后不带\n的printf后面加fflush(stdout); 在每个不想受接收缓冲区旧内容影响的scanf前面加rewind(stdin); 另外请检查scanf的返回值。
//请今后要用
char c;
scanf("%c",&c);
//时,都改为
char s[2];
char c;
scanf("%1s",s);
c=s[0];
//自动跳过一个或多个空格或Tab字符或回车换行,读取下一个字符。
仅供参考:
//NAME: essaie bla bla
//DIMENSION: 8
//DATA
//1  14  15
//2  11  10
//3  6   4
//4  7   13
//5  9   21
//6  19  3
//7  1   5
//8  8   8
//EOF
//
// 文本文件中可能还含有其他内容,但是需要用到的内容即以上

//比如data.txt:
//NAME: essaie bla bla
//其它内容
//DIMENSION: 8
//其它内容
//DATA
//其它内容
//1  14  15
//其它内容
//2  11  10
//其它内容
//3  6   4
//其它内容
//4  7   13
//其它内容
//5  9   21
//其它内容
//6  19  3
//其它内容
//7  1   5
//其它内容
//8  8   8
//其它内容
//EOF

// 目标是要获取NAME后字串,DIMENSION后数值,以及DATA以下的数值
// 其中NAME就是随便个字句,DIMENSION是城市数量,DATA以下是城市编号,X坐标,Y坐标
// 所有的这些将赋值给一个事先定义好的结构
#include <stdio.h>
#include <string.h>
#define MAXCPL   80   //每行最大字符数
#define MAXCITY  100  //每组数据中DATA最多项数,DIMENSION的最大值
#define MAXNAMEL 32   //NAME最大长度
struct S {
    char NAME[MAXNAMEL+1];
    int  DIMENSION;
    struct D {
        int NO;
        int X;
        int Y;
    } DATA[MAXCITY];
} s;
FILE *f;
int st,n,i;
char ln[MAXCPL];
int main() {
    f=fopen("data.txt","r");
    if (NULL==f) {
        printf("Can not open file data.txt!\n");
        return 1;
    }
    st=0;
    n=0;
    while (1) {
        if (NULL==fgets(ln,MAXCPL,f)) break;
        if (st==0) {
            if (1==sscanf(ln,"NAME: %31[^\n]",s.NAME)) st=1;
        } else if (st==1) {
            if (1==sscanf(ln,"DIMENSION: %d",&s.DIMENSION)) st=2;
        } else if (st==2) {
            if (0==strcmp(ln,"DATA\n")) st=3;
        } else if (st==3) {
            if (3==sscanf(ln,"%d%d%d",&s.DATA[n].NO,&s.DATA[n].X,&s.DATA[n].Y)) {
                n++;
                if (n>=MAXCITY || n>=s.DIMENSION) break;
            }
        }
    }
    fclose(f);
    printf("s.NAME=[%s]\n",s.NAME);
    printf("s.DIMENSION=%d\n",s.DIMENSION);
    for (i=0;i<n;i++) {
        printf("s.DATA[%d].NO,X,Y=%d,%d,%d\n",i,s.DATA[i].NO,s.DATA[i].X,s.DATA[i].Y);
    }
    return 0;
}
//s.NAME=[essaie bla bla]
//s.DIMENSION=8
//s.DATA[0].NO,X,Y=1,14,15
//s.DATA[1].NO,X,Y=2,11,10
//s.DATA[2].NO,X,Y=3,6,4
//s.DATA[3].NO,X,Y=4,7,13
//s.DATA[4].NO,X,Y=5,9,21
//s.DATA[5].NO,X,Y=6,19,3
//s.DATA[6].NO,X,Y=7,1,5
//s.DATA[7].NO,X,Y=8,8,8

再供参考:
#include <stdio.h>
char s[]="123 ab 4";
char *p;
int v,n,k;
void main() {
    p=s;
    while (1) {
        k=sscanf(p,"%d%n",&v,&n);
        printf("k,v,n=%d,%d,%d\n",k,v,n);
        if (1==k) {
            p+=n;
        } else if (0==k) {
            printf("skip char[%c]\n",p[0]);
            p++;
        } else {//EOF==k
            break;
        }
    }
    printf("End.\n");
}
//k,v,n=1,123,3
//k,v,n=0,123,3
//skip char[ ]
//k,v,n=0,123,3
//skip char[a]
//k,v,n=0,123,3
//skip char[b]
//k,v,n=1,4,2
//k,v,n=-1,4,2
//End.
自信男孩 2018-03-16
  • 打赏
  • 举报
回复
引用 6 楼 shihengzhen101 的回复:
[quote=引用 4 楼 cfjtaishan 的回复:] [quote=引用 3 楼 shihengzhen101 的回复:] 试了一下,基本上和 “自信男孩”的说法一致,读取时需要指定读取的字节大小,但有一点不能认同 单独读取一个int类型时可以不指定后面的字节,也可以指定 但是如果int后面还有读取内容时,就不能加字节大小 也就是说这个字节大小是指给字符数组准备的,其他的类型不需要,也不能加,所以说
scanf_s ("%d,%d,%lf,%c,%f",&a, 4, &b,4, &c, 8, &d, 1, &e, 4);
是不行的,只把字符类型的添加大小就可以 也就是写成下面这样:
scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
但是我试了一下,写成

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 2, &e);
也可以,应该是被逗号分隔了,如果是&d后面的字节数和实际输入的字节数不同,就会影响后面的读取, 所以这个问题的正确处理应该是

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
你是否试过,每种情况都在vs里编译测试过? 我是Linux系统的,没有试。之前遇到过有提出这样问题的,解决方法类似,另外,网上也有scanf_s的使用和说明[/quote] 使用的数据就是
10,-10, 8.123678309,a,3.14
[/quote]
AlbertS 2018-03-16
  • 打赏
  • 举报
回复
引用 4 楼 cfjtaishan 的回复:
[quote=引用 3 楼 shihengzhen101 的回复:] 试了一下,基本上和 “自信男孩”的说法一致,读取时需要指定读取的字节大小,但有一点不能认同 单独读取一个int类型时可以不指定后面的字节,也可以指定 但是如果int后面还有读取内容时,就不能加字节大小 也就是说这个字节大小是指给字符数组准备的,其他的类型不需要,也不能加,所以说
scanf_s ("%d,%d,%lf,%c,%f",&a, 4, &b,4, &c, 8, &d, 1, &e, 4);
是不行的,只把字符类型的添加大小就可以 也就是写成下面这样:
scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
但是我试了一下,写成

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 2, &e);
也可以,应该是被逗号分隔了,如果是&d后面的字节数和实际输入的字节数不同,就会影响后面的读取, 所以这个问题的正确处理应该是

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
你是否试过,每种情况都在vs里编译测试过? 我是Linux系统的,没有试。之前遇到过有提出这样问题的,解决方法类似,另外,网上也有scanf_s的使用和说明[/quote] 使用的数据就是
10,-10, 8.123678309,a,3.14
AlbertS 2018-03-16
  • 打赏
  • 举报
回复
我在windows+VS2008的环境下测试的,
scanf_s ("%d,%d,%lf,%c,%f",&a, 4, &b,4, &c, 8, &d, 1, &e, 4);
崩溃
scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
正常
自信男孩 2018-03-16
  • 打赏
  • 举报
回复
引用 3 楼 shihengzhen101 的回复:
试了一下,基本上和 “自信男孩”的说法一致,读取时需要指定读取的字节大小,但有一点不能认同 单独读取一个int类型时可以不指定后面的字节,也可以指定 但是如果int后面还有读取内容时,就不能加字节大小 也就是说这个字节大小是指给字符数组准备的,其他的类型不需要,也不能加,所以说
scanf_s ("%d,%d,%lf,%c,%f",&a, 4, &b,4, &c, 8, &d, 1, &e, 4);
是不行的,只把字符类型的添加大小就可以 也就是写成下面这样:
scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
但是我试了一下,写成

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 2, &e);
也可以,应该是被逗号分隔了,如果是&d后面的字节数和实际输入的字节数不同,就会影响后面的读取, 所以这个问题的正确处理应该是

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
你是否试过,每种情况都在vs里编译测试过? 我是Linux系统的,没有试。之前遇到过有提出这样问题的,解决方法类似,另外,网上也有scanf_s的使用和说明
AlbertS 2018-03-16
  • 打赏
  • 举报
回复
试了一下,基本上和 “自信男孩”的说法一致,读取时需要指定读取的字节大小,但有一点不能认同 单独读取一个int类型时可以不指定后面的字节,也可以指定 但是如果int后面还有读取内容时,就不能加字节大小 也就是说这个字节大小是指给字符数组准备的,其他的类型不需要,也不能加,所以说
scanf_s ("%d,%d,%lf,%c,%f",&a, 4, &b,4, &c, 8, &d, 1, &e, 4);
是不行的,只把字符类型的添加大小就可以 也就是写成下面这样:
scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
但是我试了一下,写成

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 2, &e);
也可以,应该是被逗号分隔了,如果是&d后面的字节数和实际输入的字节数不同,就会影响后面的读取, 所以这个问题的正确处理应该是

scanf_s ("%d,%d,%lf,%c,%f",&a, &b, &c, &d, 1, &e);
自信男孩 2018-03-16
  • 打赏
  • 举报
回复
scanf_s()函数是Microsoft公司VS开发工具提供的一个功能相同的安全标准输入函数,从vc++2005开始,VS系统提供了scanf_s()。在调用该函数时,必须提供一个数字以表明最多读取多少位字符 另外,很多带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免引用到不存在的元素,防止hacker利用原版的不安全性(漏洞)黑掉系统。 简单的理解,就是scanf_s会比scanf更安全,那么为了安全也需要编程者多传一些参数,这些参数就是变量的长度(占用的字节数)
scanf_s ("%d,%d,%lf,%c,%f",&a,4,,&b, 4, &c, 8, &d,1, &e, 4);
一般4字节的可以不指定,默认32位系统是按照4字节存储的。

33,311

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 新手乐园
社区管理员
  • 新手乐园社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧