C++常考知识点总结

0 Offer(新的一天新的难过


C++中引用与指针的区别

联系:引用是变量的别名,可以将引用看做操作受限的指针。

区别:

  • 指针是一个实体,而引用仅是个别名。
  • 引用只能在定义时必须初始化,指针可以不初始化为空。
  • 用初始化之后其地址就不可改变(即始终作该变量的别名直至销毁,即从一而终。注意:并不表示引用的值不可变,因为只要所指向的变量值改变。引用的值也就改变了),但指针所指地址是可变的。

C/C++程序的内存分区

栈区(Stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。

堆区(Heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

全局区(静态区静态static):全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放。

文字常量区 (Stack):常量字符串就是放在这里的。 程序结束后由系统释放。

程序代码区(Stack):存放函数体的二进制代码。

栈区和堆区的区别

  • 堆和栈中的存储内容:栈存局部变量、函数参数等。堆存储使用new、malloc申请的变量等。
  • 申请方式:栈内存由系统分配,堆内存由自己申请。
  • 申请后系统的响应栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表 中删除,并将该结点的空间分配给程序。
  • 申请大小的限制:Windows下栈的大小一般是2M,堆的容量较大。
  • 申请效率的比较:栈由系统自动分配,速度较快。堆使用new、malloc等分配,较慢。

总结:栈区优势在处理效率,堆区优势在于灵活。


内存模型:自由区、静态区、动态区

根据c/c++对象生命周期不同,c/c++的内存模型有三种不同的内存区域,即:自由存储区,动态区、静态区。

  • 自由存储区:局部非静态变量的存储区域,即平常所说的栈。
  • 动态区: 用new ,malloc分配的内存,即平常所说的堆。
  • 静态区:全局变量,静态变量,字符串常量存在的位置。

注意:代码虽然占内存,但不属于c/c++内存模型的一部分。


malloc和new的区别

  • 属性new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。
  • 参数:使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸(字节数)。
  • 返回类型new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回 $\rm void$ ,需要通过强制类型转换将$\rm void$指针转换成我们需要的类型。
  • 分配失败: new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
  • 自定义类型: new 会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
  • 重载:C++允许重载new/delete操作符,而malloc不允许重载。
  • 内存区域:new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。(自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆。
  • 构造函数和析构函数:malloc仅仅分配内存空间,free仅仅回收空间,不具备调用构造函数和析构函数功能,用malloc分配空间存储类的对象存在风险;new和delete除了分配回收功能外,还会调用构造函数和析构函数

常引用

  • 常引用下,原变量值不会被别名所修改。
  • 常引用可以理解为常量指针,形式为const typename & refname = varname。
  • 原变量的值可以通过原名修改。
  • 常引用通常用作只读变量别名或是形参传递。

常量指针和指针常量区别

  • 常量指针是一个指针,读成常量的指针,指向一个只读变量。如int const p或const int p。
  • 指针常量是一个不能给改变指向的指针。如int *const p。

变量声明和定义区别

  • 声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。
  • 相同变量可以再多处声明(外部变量extern),但只能在一处定义。

野指针

  • 也叫空悬指针,不是指向null的指针,是指向垃圾内存的指针。
  • 产生原因及解决办法
    • 指针变量未及时初始化 => 定义指针变量及时初始化,要么置空。
    • 指针free或delete之后没有及时置空 => 释放操作后立即置空。

堆和栈的区别

  • 申请方式不同: 栈由系统自动分配。堆由程序员手动分配。
  • 申请大小限制不同: 栈顶和栈底是之前预设好的,大小固定,可以通过ulimit -a查看,由ulimit -s修改。
  • 堆向高地址扩展,是不连续的内存区域,大小可以灵活调整。
  • 申请效率不同:栈由系统分配,速度快,不会有碎片。堆由程序员分配,速度慢,且会有碎片。

用户态切换到内核态的 3 种方式

  • 系统调用
  • 异常
  • 外围设备的中断