十八万字吐血整理的C/C++、嵌入式常见面试题!!!!

欢迎订阅,希望能点个赞!!!!

正在持续更新!!!!!欢迎探讨!!!

完整专栏地址:https://blog.nowcoder.net/zhuanlan/gmPWX0


相关知识点都能零星在网上找到,这个文章系列将目前遇到的所有常见面试问题进行一个汇总。

文中很多资料避免不了从网上或是其他复习资料里收集整理,十分感谢前辈的辛勤付出,如果存在侵权请一定联系我进行删除

也有相当一部分是本人在经历提前批以及秋招的过程中遇到和验证过的。


系列文章PDF下载地址:《最全C_C++及嵌入式软开面试题宝典.pdf》



96、内存泄漏

1.内存泄漏

内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上消失,而是应用程序分配某块内存后,由于设计错误,失去了对该段内存的控制;或者申请了⼀块内存空间,使⽤完毕后没有释放掉,或者由程序申请的⼀块内存,且没有任何⼀个指针指向它,那么这块内存就泄漏了。⼀般表现⽅式是程序运⾏时间越⻓,占⽤内存越多,最终⽤尽全部内存,整个系统崩溃。

2.内存泄漏的后果

只发生一次小的内存泄漏可能不被注意,但泄漏大量内存的程序将会出现各种征兆:性能下降到内存逐渐用完,导致另一个程序失败,而使用户无从查找问题的真正根源。

3.内存泄漏类型

(1)堆内存泄漏 (Heap leak)。对内存指的是程序运行中根据需要分配通过malloc,realloc new等从堆中分配的一块内存,再是完成后必须通过调用对应的 free或者delete 删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak.

(2)系统资源泄露(Resource Leak)。主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。

4.如何排除

使用工具软件BoundsChecker,BoundsChecker是一个运行时错误检测工具,它主要定位程序运行时期发生的各种错误;

调试运行DEBUG版程序,运用以下技术:CRT(C run-time libraries)、运行时函数调用堆栈、内存泄漏时提示的内存分配序号(集成开发环境OUTPUT窗口),综合分析内存泄漏的原因,排除内存泄漏。

5.解决方法

智能指针。

6.检查、定位内存泄漏

1.window上检查方法:在main函数最后面一行,加上一句_CrtDumpMemoryLeaks()。调试程序,自然关闭程序让其退出,查看输出:

输出这样的格式{453}normal block at 0x02432CA8,868 bytes long

{}包围的453就是我们需要的内存泄漏定位值,868 bytes long就是说这个地方有868比特内存没有释放。

定位代码位置

main函数第一行加上_CrtSetBreakAlloc(453);意思就是在申请453这块内存的位置中断。然后调试程序,程序中断了,查看调用堆栈。加上头文件#include <crtdbg.h>

2.linux:⾸先可以通过观察猜测是否可能发⽣内存泄漏,Linux中使⽤ swap 命令观察还有多少可⽤的交换空间,在⼀两分钟内键⼊该命令三到四次,看看可⽤的交换区是否在减少。

3.还可以使⽤其他⼀些 /usr/bin/stat ⼯具如 netstat vmstat 等。如发现波段有内存被分配且从不释放,⼀个可能的解释就是有个进程出现了内存泄漏。

3.⽤于内存调试,内存泄漏检测以及性能分析的软件开发⼯具 valgrind 这样的⼯具来进⾏内存泄漏的检测。

97、内存溢出定位

如果是Windows,考虑下Visual Leak Detector,这个对泄露点信息定位相当不错;如果是Linux,考虑下Valgrind,检测内存使用以及线程方面的Bug功能非常强大

98、什么是内存对齐

现在计算机内存空间都是按照byte字节划分的,理论上讲对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址上访问,这就需要各种数据类型按照一定的规则在空间上排列,而不是一个接一个的排放,这就是内存对齐。

99、为什么内存对齐

1.平台原因(移植原因)

2.不是所有的硬件平台都能访问任意地址上的任意数据的;

3.某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异

4.性能原因:

  • 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
  • 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

100、内存对齐规则

在不用#pagrama pack()包裹的情况下,结构体或联合体按照编译器默认的对齐方式有以下三个对其原则:

1.数据成员对齐原则:结构(structunion)的数据成员,第一个数据成员存放在offset0的地方,以后每个数据成员存储的起始位置都要从其占用内存大小的整数倍开始。

2.结构体作为成员的原则:如果一个结构中有某些结构体成员,则结构的成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里有struct bb里有char,int,double等元素,那b应该从8的整数倍开始,当然了,如果struct b在结构体a开始位置,则直接考虑其大小)

3.结构(或联合)的整体对齐原则:在数据成员各自对齐后,结构(或联合)本身也要进行对齐,即以结构体内部占用内存空间最大的数据类型进行对齐。(等同于sizeof该结构体的结果必须是其内部最大成员占用内存的整数倍)。

示例1
struct mystruct
{
    char a;//偏移量为0;a占用一个字节
    double b;//下一个可用地址偏移量为1,不是sizeof(double)=8的整数倍,需要补7个字节
    int c;//下一个可用地址偏移量为1+7+8=16,是sizeof(int)=4的整数倍,满足Int的对齐方式
}//所有成员变量都分配了空间,空间大小=1+7+8+4=20,不是最大空间类型double的整数倍,所以需要填充4个字节以满足结构体大小为sizeof(double)=8的整数倍
sizeof(mystruct)=24;
示例2
struct B {
int a;
char b;
int c;
};

struct A {
     char x1;
     B b;
            short x2;
     float x3;
     char x4;
}aa;

//#pragma pack()
int main() {
  printf("%d\n", sizeof(struct A));
  //printf("%x,%x,%x\n", aa.a, *(&aa.b + 1), aa.c);
  //printf("%p,%p,%p\n", &aa.a, &aa.b, &aa.c);
  return 0;
}
添加了#pragma pack(n)后规则就变成了下面这样:

1、 偏移量要是n和当前变量大小中较小值的整数倍

2、 整体大小要是n和最大变量大小中较小值的整数倍

3 n值必须为1,2,4,8…,为其他值时就按照默认的分配规则