既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
空间大小不同: 在32位OS下,堆内存可达到4GB的的空间,而栈就小得可怜.(VC6中,栈默认大小是1M,当然,你可以修改它)
能否产生碎片不同:对于栈来说,进栈/出栈都有着严格的顺序(先进后出),不会产生碎片;而堆频繁的new/delete,会造成内存空间的不连续,容易产生碎片.
生长方向不同:栈向下生长,以降序分配内存地址;堆向上生长,以升序分配内在地址.
分配方式不同:堆动态分配,无静态分配;栈分为静态分配和动态分配,比如局部变量的分配,就是动态分配(alloca函数),函数参数的分配就是动态分配(我想的…).
分配效率不同:栈是系统提供的数据结构,计算机会在底层对栈提供支持,进栈/出栈都有专门的指令,这就决定了栈的效率比较高.堆则不然,它由C/C++函数库提供,机制复杂,堆的效率要比栈低得多.
可以看出,栈的效率要比堆高很多,所以,推荐大家尽量用栈.不过,虽然栈有如此多的好处,但远没有堆使用灵活.
14 . 常用的设计模式
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点;
工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
参考:https://www.cnblogs.com/Y1Focus/p/6707121.html
https://www.zhihu.com/question/34574154?sort=created
15.手写strcpy,memcpy,strcat,strcmp等函数
https://blog.youkuaiyun.com/gao1440156051/article/details/51496782
https://blog.youkuaiyun.com/wilsonboliu/article/details/7919773
16.i++是否为原子操作?
不是。操作系统原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断,分为两种情况(两种都应该满足)
(1) 在单线程中, 能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。
(2) 在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。
i++分为三个阶段:
内存到寄存器
寄存器自增
写回内存
这三个阶段中间都可以被中断分离开.
17.有关数组,指针,函数的三者结合问题
数组指针和指针数组的区别:https://blog.youkuaiyun.com/men_wen/article/details/52694069
右左法则的说明:http://www.cnblogs.com/zhangjing0502/archive/2012/06/08/2542059.html
指针常量和常量指针:https://www.zhihu.com/question/19829354
https://blog.youkuaiyun.com/xingjiarong/article/details/47282563
注意:所谓指向常量的指针或引用(即常量引用、常量指针),不过是指针或引用“自以为是”罢了,它们觉得自己指向了常量,所以自觉地不去改变所指对象的值,但这些对象却可以通过其他途径改变。
const int *a; 等价于int const *a; const在前面所以内容不可以改变,但是指针指向可以改变。也就是常量指针
int *const a; 表示的是指针指向不可改变,但是指针所存放的内容可以改变,也即是指针常量
补充:
18.C++中类与结构体的区别?
- 最本质的一个区别就是默认的访问控制: struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。
- “class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。
19.析构函数的作用?
析构函数是用来释放所定义的对象中使用的指针,默认的析构函数不用显示调用,自建的析构函数要在程序末尾调用。
如果你的类里面只用到的基本类型,如int char double等,系统的默认析构函数其实什么都没有做
但如果你使用了其他的类如vector,string等,系统的默认析构函数就会调用这些类对象的析构函数
如果是自己写析构函数的话,如果你的类里面分配了系统资源,如new了内存空间,打开了文件等,那么在你的析构函数中就必须释放相应的内存空间和关闭相关的文件;这样系统就会自动调用你的析构函数释放资源,避免内存泄漏
例如:
1. class A
2. {
3. private:
4. char \*data;
5. public:
6. A()
7. {
8. data =
new
char[
10];
9. }
10. ~A()
11. {
12. delete[] data;
13. }
14. };
A a;
a 中将 new 10个 char
当 a 这个变量消亡的时候,将自动执行 ~A(),释放空间
对象消亡时,自动被调用,用来释放对象占用的空间,避免内存泄漏
20.虚函数的作用?
[虚函数](https://bbs.youkuaiyun.com/topics/618679757)可以让成员函数操作一般化,**用基类的指针指向不同的**[**派生类**](https://bbs.youkuaiyun.com/topics/618679757)**的对象时**,基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数,而不是基类中定义的成员函数(只要[派生类](https://bbs.youkuaiyun.com/topics/618679757)改写了该成员函数)。若不是[虚函数](https://bbs.youkuaiyun.com/topics/618679757),则不管基类指针指向的哪个[派生类](https://bbs.youkuaiyun.com/topics/618679757)对象,调用时都会调用基类中定义的那个函数。虚函数是C++多态的一种表现,可以进行灵活的动态绑定。
重点可参考:https://www.cnblogs.com/wangxiaobao/p/5850949.html
http://www.cnblogs.com/fanzhidongyzby/archive/2013/01/14/2859064.html
21.操作系统和编译器如何区分全局变量和局部变量?
操作系统只管调度进程,编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在栈里面 。
22. Makefile文件的作用?
makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
23.结构体和联合体的区别?
结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。
24.列表初始化问题?
使用初始化列表主要是基于性能问题,对于内置类型,如int, float等,使用初始化列表和在构造函数体内初始化差别不是很大;但是对于类类型来说,最好使用初始化列表。这样就可以直接调用拷贝构造函数初始化,省去了一次调用默认构造函数的过程。
1. struct Test1
2. {
3. Test1()
// 无参构造函数
4. {
5. cout <<
"Construct Test1" <<
endl ;
6. }
7.
8. Test1(
const Test1& t1)
// 拷贝构造函数
9. {
10. cout <<
"Copy constructor for Test1" <<
endl ;
11. this->a = t1.a ;
12. }
13.
14. Test1&
operator = (
const Test1& t1)
// 赋值运算符
15. {
16. cout <<
"assignment for Test1" <<
endl ;
17. this->a = t1.a ;
18. return \*
this;
19. }
20.
21. int a ;
22. };
23.
24. struct Test2 //普通初始化
25. {
26. Test1 test1 ;
27. Test2(Test1 &t1)
28. {
29. test1 = t1 ;
30. }
31. };
1. struct Test2 //2.列表初始化
2. {
3. Test1 test1 ;
4. Test2(Test1 &t1):test1(t1){}
5. }
1. Test1 t1 ;
//调用
2. Test2 t2(t1) ;
普通初始化:
列表初始化:
下列情况一定要使用初始化成员列表
- 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
- 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
- 需要初始化的数据成员是对象的情况
参考地址:https://www.cnblogs.com/weizhixiang/p/6374430.html
25. 重载与重写的区别?
从定义上来说:重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。重写:是指子类重新定义父类虚函数的方法。
从实现原理上来说:重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。重写:当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。
补充:“隐藏”是指派生类的函数屏蔽了与其同名的基类函数。规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
26.类型安全以及C++中的类型转换?
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图访问自己没被授权的内存区域。C只在局部上下文中表现出类型安全,比如试图从一种结构体的指针转换成另一种结构体的指针时,编译器将会报告错误,除非使用显式类型转换。然而,C中相当多的操作是不安全的。
详情可以移步:https://blog.youkuaiyun.com/chengonghao/article/details/50974022
四种类型转换:
- static_cast <T*> (content) 静态转换.在编译期间处理,可以实现C++中内置基本数据类型之间的相互转换。如果涉及到类的话,static_cast只能在有相互联系的类型中进行相互转换,不一定包含虚函数。
- dynamic_cast<T*>(content) 动态类型转换;也是向下安全转型;是在运行的时候执行;基类中一定要有虚函数,否则编译不通过。在类层次间进行上行转换时(如派生类指针转为基类指针),dynamic_cast和static_cast的效果是一样的。在进行下行转换时(如基类指针转为派生类指针),dynamic_cast具有类型检查的功能,比static_cast更安全。
- const_cast<T*>(content) 去常转换;编译时执行;
- reinterpret_cast<T*>(content) 重解释类型转换;
详情可以移步:https://blog.youkuaiyun.com/u010025211/article/details/48626687
https://blog.youkuaiyun.com/xtzmm1215/article/details/46475565
https://blog.youkuaiyun.com/xingkongfenqi/article/details/49148885
27.内存对齐的原则以及作用?
- 结构体内的成员按自身长度自对齐(32位机器上,如char=1,short=2,int=4,double=8),所谓自对齐是指该成员的起始地址必须是它自身长度的整数倍。如int只能以0,4,8这类地址开始。
- 结构体的总大小为结构体的有效对齐值的整数倍(默认以结构体中最长的成员长度为有效值的整数倍,当用#pragrma pack(n)指定时,以n和结构体中最长的成员的长度中较小者为其值)。即sizeof的值,必须是其内部最大成员的整数倍,不足的要补齐。
例如:
1. class A
2. {
3. char c;
4. int a;
5. char d;
6. };
7.
8. cout <<
sizeof(A) <<
endl;
9.
10. class B
11. {
12. char c;
13. char d;
14. int a;
15. };
16.
17. cout <<
sizeof(B) <<
endl;
sizeof(A)=12,sizeof(B)=8;
因为左边是1+(3)+4+1+(3)=12,而右边是1+1+(2)+4=8。括号中为补齐的字节。
内存对齐的作用:
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
详情可以移步:https://blog.youkuaiyun.com/chy19911123/article/details/48894579
28.关键字registr,typdef的作用?
register关键字的作用:
请求CPU尽可能让变量的值保存在CPU内部的寄存器中,减去CPU从内存中抓取数据的时间,提高程序运行效率。
使用register关键字应注意什么?
1.只有局部变量才可以被声明用register修饰
(register不能修饰全局变量和函数的原因:全局变量可能被多个进程访问,而用register修饰的变量,只能被当前进程访问)
2.不能用取地址获取用register修饰的变量的地址(原因:变量保存在寄存器中,而取地址获取的地址的是内存的地址)
- 用register修饰的变量一定要是CPU所接受的数据类型
typedef关键字的作用:
给数据类型定义一个新名字,
1. 提高了移植性
2. 简化复杂的类型声明,提高编码效率
3. 解释数据类型的作用
29.什么情况下需要将析构函数定义为虚函数?
当基类指针指向派生类的对象(多态性)时。如果定义为虚函数,则就会先调用该指针指向的派生类析构函数,然后派生类的析构函数再又自动调用基类的析构函数,这样整个派生类的对象完全被释放。如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。所以,将析构函数声明为虚函数是十分必要的。
详情可以移步:https://blog.youkuaiyun.com/jiadebin890724/article/details/7951461
30.有关纯虚函数的理解?
纯虚函数是为你的程序制定一种标准,纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。
1. class A{
2. protected:
3. void foo();
//普通类函数
4. virtual void foo1();
//虚函数
5. virtual void foo2() =
0;
//纯虚函数
6. }
带纯虚函数的类抽象类,这种类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。
虚函数是为了继承接口和默认行为
纯虚函数只是继承接口,行为必须重新定义
(在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常 理。所以引入了纯虚函数的概念)
详情可以参考:https://blog.youkuaiyun.com/ybhjx/article/details/51788396
30.基类指针指向派生类,派生类指针指向基类?
基类指针可以指向派生类对象,从而实现多态,例如:
1. #include <iostream>
2. using
namespace
std;
3. class Shape {
4. public:
5. virtual double area() const =
0;
//纯虚函数
6. };
7. class Square :
public Shape {
8. double size;
9. public:
10. Square(
double s) {
11. size = s;
12. }
13. virtual double area() const {
14. return size \* size;
15. }
16. };
17.
18. class Circle :
public Shape {
19. double radius;
20. public:
21. Circle(
double r) {
22. radius = r;
23. }
24. virtual double area() const {
25. return
3.14159 \* radius \* radius;
26. }
27. };
28. int main()
29. {
30. Shape\*
array[
2];
//定义基类指针数组
31. Square Sq(2.0);
32. Circle Cir(1.0);
33. array[
0] = &Sq;
34. array[
1] =&Cir;
35. for (
int i =
0; i <
2; i++) /
36. {
37. cout <<
array[i]->area() <<
endl;
38. }
39. return
0;
40. }
上面的不同对象Sq,Cir(来自继承同一基类的不同派生类)接受同一消息(求面积,来自基类的成员函数area()),但是却根据自身情况调用不同的面积公式(执行了不同的行为,它是通过虚函数实现的)。我们可以理解为,继承同一基类的不同派生对象,对来自基类的同一消息执行了不同的行为,这就是多态,它是通过继承和虚函数实现的。而接受同一消息的实现就是基于基类指针。
但是要注意的是,这个指针只能用来调用基类的成员函数。
如果试图通过基类指针调用派生类才有的成员函数,则编译器会报错。
为了避免这种错误,必须将基类指针强制转化为派生类指针。然后派生类指针可以用来调用派生类的功能。这称为向下强制类型转换,这是一种潜在的危险操作。
派生类指针不可以指向基类对象,例如:
有个people类是基类,成员有姓名和[身份证号](https://bbs.youkuaiyun.com/topics/618679757),有个派生类学生student,添加了成员学号,现在如果你说的这个情况成立student的指针----pt让他指向people成员t,则t只有两个成员变量,而*pt有3个,现在pt->学号这个变量在pt下是可以使用的,但它指向的实体却没有这个变量,所以出错,于是C++直接就避免了这样的隐式转换。
所以根据上述信息我们可以知道:
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)是不安全的。
参考链接:https://blog.youkuaiyun.com/flyingbird_sxf/article/details/41358737
https://www.cnblogs.com/rednodel/p/5800142.html
31. 继承机制中引用和指针之间如何转换?
基类——>派生类:用dynamic_cast转换(显示转换),首先检查基类指针(引用)是否真正指向一个派生类对象,然后再做相应处理,对指针进行dynamic_cast,成功返回派生类对象,失败返回空指针,对引用进行dynamic_cast,成功返回派生类对象,失败抛出一个异常。 不允许隐式转换。
派生类——>基类:可以用dynamic_cast或者直接进行类型转换(直接赋值)。
32.c语言和c++有什么区别?
C语言是结构化的编程语言,它是面向过程的,而C++是面向对象的。
封装:将数据和函数等集合在一个单元中(即类)。被封装的类通常称为抽象数据类型。封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
继承:继承主要实现重用代码,节省开发时间。它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向派生类的基类指针,来调用实现派生类中的方法。有编译时多态和运行时多态。
33.C++中的公有,私有,保护的问题?
类 | 类对象 | 公有继承派生类 | 私有继承派生类 | 保护继承派生类 | 公有继承派生类对象 | 私有继承派生类对象 | 保护继承派生类对象 | |
公有成员 | √ | √ | √ | √ | √ | √ | X | X |
私有成员 | √ | X | X | X | X | X | X | X |
保护成员 | √ | X | √ | √ | √ | X | X | X |
√:代表可以访问,X代表不能访问。
参考链接:https://zhidao.baidu.com/question/551075894.html
34.如何实现类对象只能静态分配或动态分配?
C++中建立类的对象有两种方式:
(1)静态建立,例如 A a;
静态建立一个类对象,就是由编译器为对象在栈空间中分配内存。使用这种方法,是直接调用类的构造函数。
(2)动态建立,例如 A* p = new A();
动态建立一个类对象,就是使用new运算符为对象在堆空间中分配内存。这个过程分为两步:第一步执行operator new( )函数,在堆空间中搜索一块内存并进行分配;第二步调用类的构造函数构造对象。这种方法是间接调用类的构造函数。
只能动态分配:
其实,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性(其实不光是析构函数,只要是非静态的函数,编译器都会进行检查)。如果类的析构函数在类外部无法访问,则编译器拒绝在栈空间上为类对象分配内存。
因此,可以将析构函数设为private,这样就无法在栈上建立类对象了。但是为了子类可以继承,最好设置成protected。
- class A
- {
- protected:
- A(){}
- ~A(){}
- public:
- static A* create(){return new A();}
- void destory(){delete this;}
- };
只能静态分配:
只有使用new运算符,对象才会被建立在堆上。因此只要限制new运算符就可以实现类对象只能建立在栈上。可以将new运算符设为私有。
- class A
- {
- private:
- void* operator new(size_t t){} //注意函数的第一个参数和返回值都是固定的
- void operator delete(void* ptr)() //重载了new就需要重载delete
- public:
- A(){}
- ~A(){}
- };
35.explicit关键字的作用?
C++中, 一个参数的
构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。 1 是个
构造器 ,2 是个默认且隐含的类型转换操作符。
所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候
编译器就自动调用这个构造器, 创建一个AAA的对象。
这样看起来好象很酷, 很方便。 但在某些情况下(见下面权威的例子), 却违背了我们(程序员)的本意。 这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用。
1. class Test1
2. {
3. public:
4. Test1(
int n)
5. {
6. num=n;
7. }
//普通构造函数
8. private:
9. int num;
10. };
11. class Test2
12. {
13. public:
14. explicit Test2(int n)
15. {
16. num=n;
17. }
//explicit(显式)构造函数
18. private:
19. int num;
20. };
21. int main()
22. {
23. Test1 t1=
12;
//隐式调用其构造函数,成功
24. Test2 t2=
12;
//编译错误,不能隐式调用其构造函数
25. Test2 t2(12);
//显式调用成功
26. return
0;
27. }
Test1的
构造函数带一个int型的参数,代码23行会隐式转换成调用Test1的这个构造函数。而Test2的构造函数被声明为explicit(显式),这表示不能通过隐式转换来调用这个构造函数,因此代码24行会出现编译错误。
普通构造函数能够被
隐式调用。而explicit构造函数只能被显式调用。
36.内存溢出,内存泄漏的原因?
内存溢出是指程序在申请内存时,没有足够的内存空间供其使用。原因可能如下:
- 内存中加载的数据量过于庞大,如一次从数据库取出过多数据
- 代码中存在死循环或循环产生过多重复的对象实体
- 递归调用太深,导致堆栈溢出等
- 内存泄漏最终导致内存溢出
内存泄漏是指向系统申请分配内存进行使用(new),但是用完后不归还(delete),导致占用有效内存。常见的几种情况:
(1) 在类的构造函数和析构函数中没有匹配的调用new和delete函数
两种情况下会出现这种内存泄露:一是在堆里创建了对象占用了内存,但是没有显示地释放对象占用的内存;二是在类的构造函数中动态的分配了内存,但是在析构 函数中没有释放内存或者没有正确的释放内存
(2) 在释放对象数组时在delete中没有使用方括号
方括号是告诉编译器这个指针指向的是一个对象数组,同时也告诉编译器正确的对象地址值病调用对象的析构函数,如果没有方括号,那么这个指针就被默认为只指向一个对象,对象数组中的其他对象的析构函数就不会被调用,结果造成了内存泄露。
(3)没有将基类的析构函数定义为虚函数
当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露
参考链接: https://blog.youkuaiyun.com/hyqwmxsh/article/details/52813307
缓冲区溢出(栈溢出)
程序为了临时存取数据的需要,一般会分配一些内存空间称为缓冲区。如果向缓冲区中写入缓冲区无法容纳的数据,机会造成缓冲区以外的存储单元被改写,称为缓冲区溢出。而栈溢出是缓冲区溢出的一种,原理也是相同的。分为上溢出和下溢出。其中,上溢出是指栈满而又向其增加新的数据,导致数据溢出;下溢出是指空栈而又进行删除操作等,导致空间溢出。
37.auto_ptr类与shared_ptr类?
从c++11开始, auto_ptr已经被标记为弃用, 常见的替代品为shared_ptr。shared_ptr的不同之处在于引用计数, 在复制(或赋值)时不会像auto_ptr那样直接转移所有权。 两者都是模板类,却可以像指针一样去使用。只是在指针上面的一层封装。
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露
参考链接: https://blog.youkuaiyun.com/hyqwmxsh/article/details/52813307
缓冲区溢出(栈溢出)
程序为了临时存取数据的需要,一般会分配一些内存空间称为缓冲区。如果向缓冲区中写入缓冲区无法容纳的数据,机会造成缓冲区以外的存储单元被改写,称为缓冲区溢出。而栈溢出是缓冲区溢出的一种,原理也是相同的。分为上溢出和下溢出。其中,上溢出是指栈满而又向其增加新的数据,导致数据溢出;下溢出是指空栈而又进行删除操作等,导致空间溢出。
37.auto_ptr类与shared_ptr类?
从c++11开始, auto_ptr已经被标记为弃用, 常见的替代品为shared_ptr。shared_ptr的不同之处在于引用计数, 在复制(或赋值)时不会像auto_ptr那样直接转移所有权。 两者都是模板类,却可以像指针一样去使用。只是在指针上面的一层封装。
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
[外链图片转存中…(img-ccp7WCqm-1715862641219)]
[外链图片转存中…(img-UgY3edgX-1715862641219)]
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!