啊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++已经入门的学生或人士,有一定的编程基础。

本教程适合于互联网嵌入式软件求职的学生或人士。

img

故事背景

img

蒋 豆 芽:小名豆芽,芳龄十八,蜀中人氏。卑微小硕一枚,科研领域苟延残喘,研究的是如何炒好一盘豆芽。与大多数人一样,学习道路永无止境,间歇性踌躇满志,持续性混吃等死。会点编程,对了,是面对对象的那种。不知不觉研二到找工作的时候了,同时还在忙论文,豆芽都秃了,不过豆芽也没头发啊。

隔壁老李:大名老李,蒋豆芽的好朋友,技术高手,代码女神。给了蒋豆芽不少的人生指导意见。

导 师:蒋豆芽的老板,研究的课题是每天对豆芽嘘寒问暖。

img

故事引入

img

隔壁老李:(嗝~~)豆芽,中午的满汉全席不错哈!

蒋 豆 芽:(哭唧唧)我一个月的生活费没了。

隔壁老李:没事,豆芽,吃人嘴软,学知识包在我身上了!我们继续讲内存布局。

img

1.18 内存布局

img

隔壁老李:谈到指针,又不得不提很重要的内容,那就是内存管理。说到内存管理,我们要先讲内存布局模型

蒋 豆 芽:(疑惑)内存布局模型又是个啥?

隔壁老李:豆芽,你知道C语言如何申请内存吗?

蒋 豆 芽:这个简单!C语言通过malloccalloc函数、用realloc调整内存空间大小,而C++中用的new申请内存空间。

隔壁老李:那我问问你,动态申请内存是在堆还是栈上?局部变量申请内存空间又是在哪里申请呢?它们各自的生命周期又是如何的呢?

蒋 豆 芽:emmm,这个我就不知道了诶。

隔壁老李:所以我们就很有必要讲讲内存布局模型了。理解好变量的生存周期内存分布很有必要,这会帮助我们深刻理解变量的使用,比如变量的作用域到哪里,在什么时候销毁,要是本来内存就销毁了,而我们还去使用,是不是就会出现运行错误?

蒋 豆 芽:变量的生存周期我知道!变量从定义开始分配存储单元,到运行结束存储单元被回收,整个过程称为变量的生存周期。

隔壁老李:(会心一笑)不错啊,豆芽,那我们就开始讲讲变量的内存布局模型。

img

如上图,从低地址到高地址,一个程序由代码段、数据段、BSS段、堆栈段组成。

  1. 代码段:存放程序执行代码的一块内存区域。只读,不允许修改,代码段的头部还会包含一些只读的常量,如字符串常量字面值(注意:const变量虽然属于常量,但是本质还是变量,不存储于代码段)。

  2. 数据段data:存放程序中已初始化全局变量静态变量的一块内存区域。

  3. BSS 段:存放程序中未初始化全局变量静态变量的一块内存区域。

  4. 可执行程序在运行时又会多出两个区域:堆区栈区。

    堆区:动态申请内存用。堆从低地址向高地址增长。

    栈区:存储局部变量函数参数值。栈从高地址向低地址增长。是一块连续的空间。

  5. 最后还有一个文件映射区(共享区),位于堆和栈之间。

为了直观说明,我们通过一个例子:

img

(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!豆芽,看样子你学会了!

蒋 豆 芽:(一脸嬉笑)是啊,快学废了。

img

1.19 野指针与内存泄露

img

隔壁老李:(敲脑袋)好好听讲!而指针和内存管理就关联很大。

C语言中在堆中申请内存用的malloccalloc函数、用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>

全部评论
看的真的很爽 哈哈 一口气看完了C语言的内容
1 回复 分享
发布于 2021-04-19 20:48
嘿嘿,复习了操作系统中段页式和内部碎片和外部碎片相关问题
1 回复 分享
发布于 2021-04-12 16:58
请问1.20为什么将 char p[] 改为 char *p,字符串就从栈区变到常量区了呢?
点赞 回复 分享
发布于 2021-12-24 17:57
关于这一句:BSS 段:存放程序中未初始化的全局变量和静态变量的一块内存区域。 前面3.2节中说全局变量在定义后会被系统自动初始化,那这里存在的未初始化的全局变量是不是存在歧义。
点赞 回复 分享
发布于 2021-08-18 17:07
1.19 例子3 为啥a的作用域只有大括号那一段呀?不是a b 不都在一个函数里吗
点赞 回复 分享
发布于 2021-07-05 09:28
请问,指针被释放后为什么还要置NULL才行
点赞 回复 分享
发布于 2021-06-26 15:16
1.20那节中给出的使用二级指针的例子没太理解,为什么要使用二级指针呢?
点赞 回复 分享
发布于 2021-06-26 14:40

相关推荐

09-24 18:30
已编辑
长春工业大学 产品经理
小肥罗:HR就是好人的缩写哈哈哈哈
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务