错误 1 error C4996: 'scanf': This function or variable may be unsafe. Consider using scanf_s instead
一.前言
在使用STL各类容器的时候,有时会出现迭代器失效,引用(指针)失效等情况的而发生,即使看似你的操作都是合法的情况下。
要了解问题的原因,我们就要了解C++中stl容器的内存分配策略。我们才知道在哪些操作下可能导致迭代器失效,引用(指针)失效。
二.问题分类
首先我们把以上的问题分成两类:
容器的迭代器为什么会失效?
容器元素的引用(指针)为什么会失效?
因为从内存角度上来讲,如果引用是失效了那么指针也就是失效了,也就是容器的该元素从这个内存地址搬家了!
三.第一类问题分析
问题: 容器的迭代器为什么会失效?
对于上面的问题我们可以换个说法:容器的元素在容器内部搬家了。
我们可以把容器看做是一个小镇,有一个个的房子(无论是list,vector还是其他的,只是房子之间的联系关系不同。不过这不在我们目前考虑范围);
而元素就是相当于住在房子里面的人。
现在,假如小镇又搬进来一户人家。小镇自然需要为这户人家安排一户房子:
假如是空房子还好(只需要住进去就行);
如果新来的这户人家看中了已经居住的某户人家的房子,那么肯定需要之前那户人家搬出来,然后才能进去住。而某些容器中为了保证数据的顺序一致性,就会出现下面的情况:
很显然,如果出现了以上图片所示的情况,那么就相当于容器的元素在容器内部搬家了,因为或多或少的造成了其他元素搬家。
而迭代器相当于什么呢?恩,我们可以在这里把它当做是房子的门牌号(每个房子的门牌号都不一样)。
以前我们知道通过一户人家的门牌号就可以很容易找到这户人家。但是,如果该户人家搬家了呢?
对,正是这样的”搬家”导致容器的迭代器失效。
问题原因找出来了,我们下面就来总结一下,各类容器发生迭代器失效的情况:
可以看出来非线性表的适应性是最好的,因为不需要内存地址连续。
而线性表中,不适宜的插入删除位置会使得迭代器失效。(使得迭代器失效还有一种情况:容器存储空间的重新分配,我们后面来讲)
下面我来举个例子:
给定一个容器 vector< int >vi,删除容器中所有为3的元素。
下面是两个版本的代码:
代码1:
for(auto it = vi.begin();it != vi.end();)
{
if(*it == ) it = vi.erase(it);
else it ++;
}
代码2:
auto end = vi.end();
for(auto it = vi.begin();it != end;)
{
if(*it == ) it = vi.erase(it);
else it ++;
}
可能代码2的作者是考虑到每次循环都需要调用vi.end()的开销,于是就在循环外记录了尾后迭代器。
然而在上面的代码中。由于在循环内部可能会调用erase的函数;也就是说一旦发生删除,那么尾后迭代器就会失效。所以代码2是错误的!!
不过还有个地方值得注意:删除erase的时候会使得it迭代器失效,所以需要用it来接受erase函数的返回值。
四.第二类问题分析
问题: 容器元素的引用或指针为什么会失效?
这个问题就是涉及到了不同容器中如何去管理内存的。以下用 vector(string) 和 deque 来举例。
- vector(string 可看做vector< char >)
为了支持快速随机访问,vector只能将元素连续存储——每个元素紧挨着前一个元素存储。然而当我们向vector 或者string添加元素的时候;如果没有空间容纳新元素,容器不可能简单地把它添加在内存的其他位置——因为元素必须连续存储。容器就必须重新分配新的内存空间来保存已有的元素和新元素,将旧元素移动到新空间,然后添加新的元素。
- deque
deque看似和vector很相似,但是 deque 能高效的在首位进行元素的插入删除;并且 deque 也支持随机访问;说明 deque 的内部内存实现要比vector复杂得多。事实上 deque 采用的是动态内存块的策略,块的内部是一段连续的内存,但是块与块之间物理内存不一定连续。
deque的元素数据采用分块的线性结构进行存储,如图所示。deque分成若干线性存储块,称为deque块。块的大小一般为512个字节,元素的数据类型所占用的字节数,决定了每个deque块可容纳的元素个数。
所有的deque块使用一个Map块进行管理,每个Map数据项记录各个deque块的首地址。Map是deque的中心部件,将先于deque块,依照deque元素的个数计算出deque块数,作为Map块的数据项数,创建出Map块。以后,每创建一个deque块,都将deque块的首地址存入Map的相应数据项中。
在Map和deque块的结构之下,deque使用了两个迭代器M_start和M_finish,对首个deque块和末deque块进行控制访问。迭代器iterator共有4个变量域,包括M_first、M_last、M_cur和M_node。M_node存放当前deque块的Map数据项地址,M_first和M_last分别存放该deque块的首尾元素的地址(M_last实际存放的是deque块的末尾字节的地址),M_cur则存放当前访问的deque双端队列的元素地址。
简单介绍了 vector 和 deque 的内存管理策略之后;我们知道,如果当容器中进行了内存重新分配。那么元素的物理存储地址必然改变,所以这就是导致引用和指针失效的原因!!
我们下面就来总结一下,各类容器发生引用(指针)失效的情况:
五.后话
值得注意的是,不同的编译器对于stl内存管理策略可能略有差别。下面拿 vector 举例:
我们知道 vector 的内存管理策略是:当push_back的时候发现容量不够存储新的元素就需要去开辟一个更大的内存,然后将旧元素复制过去,然后再加入新元素。
那么有个问题是:当容量不够的时候需要开辟一个多大的内存呢?这个实现机制在不同的编译器中就可能有不同。
目前我见过有三个版本的:
- VS2013自带G++编译器 : 每次开辟新内存比原来多一个元素内存。
- VS2010自带G++编译器: 每次开闭新内存比原来容量多一半。(*150%)
- CodeBlocks自带GCC编译器: 每次开辟新内存为原来容量的一倍。(*200%)
不管开辟内存的策略如何,总需要满足一个定则:
在一个初始化为空的 vector 上调用n次push_back来创建一个n个元素的vector,所花费时间不能超过n的常数倍。
还有一个值得注意的地方就是:
对于vector的 pop_back 或者 erase ,clear 只会减小容器的元素个数,并不会减少容器的容量。resize也是一样,如果小于size。只会使得容器元素个数减小,容器的容量不会减小。
换句话来说,一个vector容器对象容量是只增不减的。只会在析构函数的时候对占用内存进行释放。
当然以上问题也不是不可以解决的:
使用swap函数可以使得两个vector对象内部元素甚至所占内存空间都互换。
比如想释放vector< int > vt 占用的内存:
- 清空所有元素,释放所有容量
vector<int>().swap(vt);
- 元素不改变,释放多余的容量
vector<int>(vt).swap(vt);
从第二段代码我们也可以得知,vector 的拷贝构造函数只是会将元素拷贝过来,而容量也只是会初始化为和元素个数一样。
还值得注意的一个地方是:
如果会需要向一个vector中插入很多记录,比如说100000条,为了避免在插入过程中移动内存,咱实现向系统预订一段足够的连续的空间,例如:
a.reserve(100000);
错误 1 error C4996: 'scanf': This function or variable may be unsafe. Consider using scanf_s instead的更多相关文章
- 错误 1 error C4996: 'scanf': This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. d:\users\vs2013\le
#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>void main(){ int nu ...
- VS 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案
在VS中编译 C 语言项目,如果使用了 scanf 函数,编译时便会提示如下错误: error C4996: 'scanf': This function or variable may be uns ...
- Visual Studio 2012 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案
在VS 2012 中编译 C 语言项目,如果使用了 scanf 函数,编译时便会提示如下错误: error C4996: 'scanf': This function or variable may ...
- [转]Visual Studio 2012 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案
原文地址:http://www.cnblogs.com/gb2013/archive/2013/03/05/SecurityEnhancementsInTheCRT.html 在VS 2012 中编译 ...
- Visual Studio 2012 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案(转载)
转载:http://www.th7.cn/Program/c/201303/127343.shtml 原因是Visual C++ 2012 使用了更加安全的 run-time library rout ...
- 解决VS2015中出现类似于error C4996: 'scanf': This function or variable may be unsafe的安全检查错误
用习惯了VS老版本的人当刚使用VS2013的时候可能总遇到类似于这样的错误: error C4996: 'scanf': This function or variable may be unsafe ...
- vs2013/2015中scanf函数类似于error C4996: 'scanf': This function or variable may be unsafe的安全检查错误
在使用vs2015时,遇到了scnaf函数安全性的问题,程序不能正常运行,错误如下: error C4996: 'scanf': This function or variable may be un ...
- 错误: error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. 的处理方法
- error C4996: 'scanf': This function or variable may be unsafe.
项目属性-配置属性-c/c++-预处理器- 在下面的编辑窗口中添加一句命令:_CRT_SECURE_NO_WARNINGS 添加完成后应用并退出 http://jingyan.baidu.com/al ...
随机推荐
- Axure 简单原型设计
简介 Axure RP是一个专业的快速原型设计工具.Axure(发音:Ack-sure),代表美国Axure公司:RP则是Rapid Prototyping(快速原型)的缩写. Axure RP是美国 ...
- PySpider 框架爬虫错误 HTTP 599: SSL certificate problem: unable to get local issuer certificate解决方案
首先pyspider all启动pyspider的所有服务,然后访问http://localhost:5000创建一个爬虫任务:taobaomm,点开任务链接编辑http://localhost:50 ...
- 基于QTP的自己主动化測试框架介绍
继前面用了七章介绍了基于QTP的自己主动化測试框架,以下再用几个视频再补充一下. 视频一:基本框架特点介绍说明 .框架的特点从正反两面进行了分析以及主要思想 http://v. ...
- (转)IntelliJ IDEA下的使用git
1.git简介 Git是目前流行的分布式版本管理系统.它拥有两套版本库,本地库和远程库,在不进行合并和删除之类的操作时这两套版本库互不影响.也因此其近乎所有的操作都是本地执行,所以在断网的情况下任然可 ...
- mac软件下载
mac软件下载 http://www.pc6.com/mac/ https://www.macx.cn/
- nginx安装说明
下载地址:http://nginx.org/en/download.html 安装版本:1.10.0 安装配置如下: /etc/nginx 目录 /home/nginx目录 --prefix=/hom ...
- SOA初探
背景 曾今SOA的概念犹如今日“云计算.大数据”一样,被炒得火热,不少企业便纷纷响应,并宣称会拥抱和实施SOA.而事实上,业界出现了两种极端:一种是由于各类文章和书籍关于SOA的描述往往太过抽象,再加 ...
- Webbrowser控件判断网页加载完毕的简单方法
一般情况下,当ReadyState属性变成READYSTATE_COMPLETE时,Webbrowser控件会通过触发DocumentCompleted事件来指示网页加载完毕.但当加载的网页包含fra ...
- Java包装类的常量池
Integer a=Integer.valueOf(100); Integer b=Integer.valueOf(100); System.out.println(a==b); Double d1 ...
- java集合框架01——总体框架一览
java集合框架是java提供的工具包,在java.util.*中,这个包中包含了常用的数据结构:集合.数组.链表.栈.队列.映射等.java集合框架主要可以分为四个部分:List列表.Set集合.M ...