啊C啊C我爱你-4
C++软件与嵌入式软件面经解析大全(蒋豆芽的秋招打怪之旅)
本章讲解点
1.1 熟悉的陌生人——main函数
1.2 If else——易错细节分辨
1.3 Switch、break——相爱相杀的故事
1.4 For、while、do-while——傻傻分不清楚
1.5 枚举
1.6 结构体与共用体
1.7 函数——原来你是这样的函数
1.8 全局变量与静态变量详解
1.9 数据类型详解
1.10 类型转换
1.11 谁先谁后——把人搞疯的优先级
1.12 震惊——位运算还可以这样用
1.13 面试技巧讲解
1.14 数组指针与指针数组
1.15 指针与字符串
1.16 指针与函数
1.17 指针与结构体
1.18 内存布局
1.19 野指针与内存泄露
1.20 野指针与内存申请
1.21 内存碎片
受众:本教程适合于C/C++已经入门的学生或人士,有一定的编程基础。
本教程适合于互联网、嵌入式软件求职的学生或人士。
故事背景
蒋 豆 芽:小名豆芽,芳龄十八,蜀中人氏。卑微小硕一枚,科研领域苟延残喘,研究的是如何炒好一盘豆芽。与大多数人一样,学习道路永无止境,间歇性踌躇满志,持续性混吃等死。会点编程,对了,是面对对象的那种。不知不觉研二到找工作的时候了,同时还在忙论文,豆芽都秃了,不过豆芽也没头发啊。
隔壁老李:大名老李,蒋豆芽的好朋友,技术高手,代码女神。给了蒋豆芽不少的人生指导意见。
导 师:蒋豆芽的老板,研究的课题是每天对豆芽嘘寒问暖。
故事引入
隔壁老李:(嗝~~)豆芽,中午的满汉全席不错哈!
蒋 豆 芽:(哭唧唧)我一个月的生活费没了。
隔壁老李:没事,豆芽,吃人嘴软,学知识包在我身上了!我们继续讲内存布局。
1.18 内存布局
隔壁老李:谈到指针,又不得不提很重要的内容,那就是内存管理。说到内存管理,我们要先讲内存布局模型。
蒋 豆 芽:(疑惑)内存布局模型又是个啥?
隔壁老李:豆芽,你知道C语言如何申请内存吗?
蒋 豆 芽:这个简单!C语言通过malloc
和calloc
函数、用realloc
调整内存空间大小,而C++中用的new
申请内存空间。
隔壁老李:那我问问你,动态申请内存是在堆还是栈上?局部变量申请内存空间又是在哪里申请呢?它们各自的生命周期又是如何的呢?
蒋 豆 芽:emmm,这个我就不知道了诶。
隔壁老李:所以我们就很有必要讲讲内存布局模型了。理解好变量的生存周期和内存分布很有必要,这会帮助我们深刻理解变量的使用,比如变量的作用域到哪里,在什么时候销毁,要是本来内存就销毁了,而我们还去使用,是不是就会出现运行错误?
蒋 豆 芽:变量的生存周期我知道!变量从定义开始分配存储单元,到运行结束存储单元被回收,整个过程称为变量的生存周期。
隔壁老李:(会心一笑)不错啊,豆芽,那我们就开始讲讲变量的内存布局模型。
如上图,从低地址到高地址,一个程序由代码段、数据段、BSS段、堆栈段组成。
代码段:存放程序执行代码的一块内存区域。只读,不允许修改,代码段的头部还会包含一些只读的常量,如字符串常量字面值(注意:const变量虽然属于常量,但是本质还是变量,不存储于代码段)。
数据段data:存放程序中已初始化的全局变量和静态变量的一块内存区域。
BSS 段:存放程序中未初始化的全局变量和静态变量的一块内存区域。
可执行程序在运行时又会多出两个区域:堆区和栈区。
堆区:动态申请内存用。堆从低地址向高地址增长。
栈区:存储局部变量、函数参数值。栈从高地址向低地址增长。是一块连续的空间。
最后还有一个文件映射区(共享区),位于堆和栈之间。
为了直观说明,我们通过一个例子:
(1)首先,A *
a:a是一个局部变量,类型为指针,故而操作系统在栈区开辟4或8字节的空间(0x000m)分配给指针a。
(2)new A
:通过new动态的在堆区申请类A大小的空间(0x000n)。
(3)a = new A
:将指针a的内存区域填入堆中类A申请到的地址。即*
(0x000m)=0x000n。
(4)a->i
:先找到指针a的地址0x000m,通过*
a的值0x000n和i在类A中偏移offset,得到a->i
的地址(0x000n+offset),进行*
(0x000n+offset) = 10的赋值操作,即内存(0x000n+offset)存储的值是10。
蒋 豆 芽:(恍然大悟)哦,原来是这样。明白了内存分布,我突然想通了很多问题了呀!难怪函数不能返回局部栈数组,因为函数运行完后局部数组就回收了。
隔壁老李:没错,所以内存分布模型很重要,你要好好掌握。我们再来看看一个例子,你看看下面的代码,变量a的生存周期是多少?
#include <stdio.h> using namespace std; double cylinder(double r, double h) { r = 1.1; h = 2.2;//改变形参值 static int a = 3; return 1.0; } int main() { double radius = 3.2; double height = 4.2; double volume = cylinder(radius, height); return 0; }
蒋 豆 芽:这个我就知道了。static
变量是专门存储在数据段或BSS
段里,会持续到程序结束为止,所以即使函数执行完了也不影响变量a
。而局部变量是存储在栈中,而函数结束后,由操作系统回收栈,那么局部变量内存空间也就被释放了。而常量在代码段里也有专门存储的地方。所以把变量存储的内存分布搞懂后,很多面试问题也就迎刃而解了。
隔壁老李:bingo!豆芽,看样子你学会了!
蒋 豆 芽:(一脸嬉笑)是啊,快学废了。
1.19 野指针与内存泄露
隔壁老李:(敲脑袋)好好听讲!而指针和内存管理就关联很大。
C语言中在堆中申请内存用的malloc
和calloc
函数、用realloc
调整内存空间大小,而C++
中用的new
申请内存空间,都是以指针变量操作的,如果不了解内存管理,就会出现各种问题,比如野指针、内存泄露。
这里我们就讲讲野指针、内存泄露的问题。
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针不同于空指针,空指针是指一个指针的值为null,而野指针的值并不为null,野指针会指向一段实际的内存,只是它指向哪里我们并不知情,或者是它所指向的内存空间已经被释放,所以在实际使用的过程中,我们并不能通过指针判空去识别一个指针是否为野指针。避免野指针只能靠我们自己养成良好的编程习惯。
蒋 豆 芽:那老李,有哪些情况下会产生野指针,以及怎样避免呢?
隔壁老李:有以下情况,我们仔细讲。
(1)指针变量的值未被初始化: 声明一个指针的时候,没有显示的对其进行初始化,那么该指针所指向的地址空间是乱指一气的。如果指针声明在全局数据区,那么未初始化的指针缺省为空,如果指针声明在栈区,那么该指针会随意指向一个地址空间。
void func(){ int *ptr; // 野指针 if (ptr != nullptr) { //这里的判空并不起作用 ……do_somthing } }
(2)指针所指向的地址空间已经被free或delete:在堆上malloc或者new出来的地址空间,如果已经free或delete,那么此时堆上的内存已经被释放,但是指向该内存的指针如果没有人为的修改过,那么指针还会继续指向这段堆上已经被释放的内存,这时还通过该指针去访问堆上的内存,就会造成不可预知的结果,给程序带来隐患。
void func(){ char *p = (char *)malloc(sizeof(char)*100); s
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> - 本专刊适合于C/C++已经入门的学生或人士,有一定的编程基础。 - 本专刊适合于互联网C++软件开发、嵌入式软件求职的学生或人士。 - 本专刊囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构等一系列知识点的讲解,并且最后总结出了高频面试考点(附有答案)共近400道,知识点讲解全面。不仅如此,教程还讲解了简历制作、笔试面试准备、面试技巧等内容。 </p> <p> <br /> </p>