目录
前言
今天我在编译程序的时候,有一个细节所所带来的数据溢出问题。
这一方面在老一点的教材和现有教材上是没有说明的,那就是Scanf带来的数据溢出问题。
这个BUG困扰我足足两周。
终于,在两周后的今天解决了。正所谓“在哪里跌倒就在那个地方继续趴着,休息好了再站起来。”
言归正传
一、“scanf”函数是什么?
scanf()是C语言中的一个输入函数。与printf函数一样,都被声明在头文件stdio.h里,因此在使用scanf函数时要加上#include <stdio.h>。(在有一些实现中,printf函数与scanf函数在使用时可以不使用预编译命令)它是格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中。
(以上词条源自“百度百科”)
int scanf(const char * restrict format,...);
这运用一个指针指向内存来读取键盘上输入的数据给编译器,但是既然是内存那么就一点有大小。那么进而带来边界问题进而带来的是数据溢出问题。数据溢出带来的就是数据安全问题。所以比"Scanf"更安全的"Scanf_s"孕育而生......
二、“Scanf_s ”函数是什么?
很多带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免引用到不存在的元素,有时黑客可以利用原版的不安全性黑掉系统。
(以上词条源自“百度百科”)
scanf_s( _In_z_ _Scanf_s_format_string_ char const* const _Format, ...)
可起初我并没有重视"scanf_s"带来的数据严谨性。一直到前两周的一个''BUG'',让我不得不重视这个问题
三、问题再现
四、问题分析
对于这个问题我在用''%d''的时候是没有问题的,但我在源代码这用了"%S"
扩展:"%s"是什么
说简单就是给编译器''输入输出字符串''的,什么是字符串呢?说白了就是''A、B、C、D、F、G''
可计算机却不认识''A、B、C、D、F、G''他们只知道''0'' or ''1''所以.
后来创造了“Ascll”码用来人们用来与机器交换自然语言。于是就在这里容易忽略一个内存问题。
接下来我用一张图来解释我想说的内容
顺着这个思路我们继续往下面看
C:\Users\Administrator\Desktop\text.txt
假定这一串字符自然长度为X ,那么翻译成机器语言的长度大于7*x
(因为ASLLC把单个的字母翻译成7位二进制数据)
此刻在机器中编译器识别你输入了''X''长度的字符串,这个时候编译器像计算机申请了''X''长度的内存;但由于''ASLLC''的编译方式,实际到达机器内存的数据达到了''7*x''个长度.这里就会造成数据溢出
这里就会有聪明的同学就会说了,那我输入一个字符就好了。那我在这里给你们试试看
所以在这里大家看出来了?
五、解决办法
很简单向机器申请足够的内存!
怎么申请!
用''Scanf_s''申请!
scanf_s("%s", intputfile,100);
就是在''scanf_s''中申请100个长度
在这里需要强调一下''scanf_s''的具体用法
(来源'''百度词条'')
读取单个字符也需要限定长度:scanf_s("%c,%c",&c1,1,&c2,1);而不能写成scanf_s("%c,%c",&c1, &c2,1, 1);否则编译器会报错
六:源代码
-
错误代码
#include<stdio.h>
int main()
{
char intputfile[100]; // 定义用于存储输入文件名字的字符数组
FILE* fp; // 定义文件指针
printf("请输入要打开文件的名字:");
scanf_s("%s", intputfile); // 输入要打开的文件名字
if (fopen_s(&fp, intputfile, "w+") != 0) // 正确使用 fopen_s 打开文件
{
printf("\n%s 打开失败!\n", intputfile);
return 0;
}
printf("%s 文件内容:\n", intputfile);
while (1) // 用一个恒真的条件进入循环
{
int ch = fgetc(fp); // 获取字符
if (ch == EOF) // 如果是文件结束标志
break; // 则退出循环
putchar(ch); // 输出字符
}
printf("\n");
fclose(fp); // 关闭文件
return 0;
}
-
正确代码
#include<stdio.h>
int main()
{
char intputfile[100]; // 定义用于存储输入文件名字的字符数组
FILE* fp; // 定义文件指针
printf("请输入要打开文件的名字:");
scanf_s("%s", intputfile,100); // 输入要打开的文件名字
if (fopen_s(&fp, intputfile, "w+") != 0) // 正确使用 fopen_s 打开文件
{
printf("\n%s 打开失败!\n", intputfile);
return 0;
}
printf("%s 文件内容:\n", intputfile);
while (1) // 用一个恒真的条件进入循环
{
int ch = fgetc(fp); // 获取字符
if (ch == EOF) // 如果是文件结束标志
break; // 则退出循环
putchar(ch); // 输出字符
}
printf("\n");
fclose(fp); // 关闭文件
return 0;
}
总结
- 内存溢出
- scanf_s带来的数据安全、
- scanf_s与scanf的区别