八股文之C/C++ 高频知识点汇总

1、new/delete和malloc/free的区别

  • malloc/free是C/C++的库函数,需要stdlib.h;new/delete是C++的关键字;
  • 都可用于申请动态内存和释放内存,new/delete在对象创建的时候自动执行构造函数,对象消亡前自动执行析构函数,底层实现其实也是malloc/free
  • new无需指定内存块的大小,编译器会根据类型信息自行计算;malloc需要显式地支持所需内存的大小
  • new返回指定类型的指针,无需进行类型转换;malloc默认返回类型为void*,必须强行转换为实际类型的指针
  • new内存分配失败时会抛出bad_alloc异常;malloc失败时返回NULL

2、malloc的底层实现

Linux下:
  • 开辟空间小于128K时,通过brk()函数
    • 将数据段.data的最高地址指针_edata向高地址移动,即增加堆的有效区域来申请内存空间
    • brk分配的内存需要等到高地址内存释放以后才能释放,这也是内存碎片产生的原因
  • 开辟空间大于128K时,通过mmap()函数
    • 利用mmap系统调用,在堆和栈之间文件映射区域申请一块虚拟内存
    • 128K限制可由M_MMAP_THRESHOLD选项进行修改
    • mmap分配的内存可以单独释放
  • 以上只涉及虚拟内存的分配,直到进程第一次访问其地址时,才会通过缺页中断机制分配到物理页中

3、指针和引用的异同点;如何相互转换

  • 本质:引用是别名,而指针是地址
  • 指针在运行时可以改变所指向的值,而引用一旦与某个对象绑定之后就不再改变(指向的地址不能改变,但指向的内容可以改变)
  • 指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值;因此指针可以改变指向的对象,而引用的对象不能修改
  • 由于硬件通过地址访问内存位置,因此引用可以理解为一个常量指针,只能绑定到初始化它的对象上

4、struct、union的异同

  • struct中每个变量依次存储;union中,每个变量都是从偏移地址零开始存储,同一时刻只有一个成员存储于该地址
  • struct内存大小遵循结构对齐原则
    • 数据成员对齐规则:每个数据成员存储的起始位置要从该成员大小的整数倍开始
    • 数据成员包含结构体:结构体成员要从其内部最大元素对象的整数倍地址开始存储
    • 结构体总大小:其内部最大基本成员的整数倍,不足则要补齐
  • union内存大小为其最大成员的整数倍

5、extern C的作用

C++支持函数重载,即不同名字空间namespace的两个函数原型声明可以完全相同,或者两个函数同名但参数列表不同;g++编译器会对此进行name mangling,生成全局唯一的符号名称,使链接器可以准确识别
C语言不支持函数重载,即不允许同名符号,所以不需要这些工作,因此在C++代码中加入extern C,是为了链接规范

6、memcpy()函数需要注意哪些问题

函数原型声明void *memcpy(void *dest, void *src, unsigned int count);
memcpy函数用于把资源内存(src所指向的内存区域)中连续的count个字节数据拷贝到目标内存(dest所指向的内存区域)
  • 数据长度count的单位是字节,1byte = 8bit
  • 数据类型为char,则数据长度就等于元素的个数;其他数据类型则要注意数据长度的值
  • n * sizeof(type_name)的写法

7、strcat、strncat、strcmp、strcpy函数

strcpy拷贝函数,不会判断拷贝大小,也没有任何安全检查,不会检查目的地址内存是否够用;
strncpy拷贝函数,会计算复制字符串的大小,但没有检查目标的边界;
strcmp比较函数,把src所指向的字符串与dest所指向的字符串进行比较,若dest与src的前n个字符相同,则返回0;若dest大于src,则返回大于0的值;若dest小于src,则返回小于0的值
strcat功能是将两个char类型连接;strncat功能是在字符串的结尾追加n个字符

8、机器大小端问题

大端指数据的高字节保存在内存的低地址中,数据的低字节保存在内存的高地址中;小端与此相反。
小端:强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样
大端:符号位的判定固定为第一个字节,很容易判断正负
union判断大小端的方法
union从低地址开始存,同一时间内只有一个成员占用内存;修改其中一个成员的值必然会影响另一个成员的值

9、static的用法(定义和用途)

static修饰局部变量:使其变为静态存储方式(静态数据区),函数执行完成之后不会被释放,而是继续保存在内存中;
static修饰全局变量:使其只在本文件内部有效,其他文件不可链接或引用该变量;
static修饰函数:静态函数,即函数只在本文件内部有效,对其他文件不可见;避免同名干扰,同时保护

10、const的用法(定义和用途)

const起到强制保护的修饰作用,可以预防意外改动,提高程序的健壮性
  • const修饰常量:定义时就初始化,以后不能更改;
  • const修饰形参:func(const int a); 该形参在函数里不能改变;
  • const修饰类成员函数:const类成员函数不能改变成员变量的数值

11、const常量和#define的区别(编译阶段、安全性、内存占用等)

  • const定义的常量有类型名字,存放在内存的静态区域中,在编译时确定其值;
  • #define定义的常量是没有类型的一个立即数,编译器会在预处理阶段将程序中所有使用到该常量的地方进行拷贝替换
  • 由于#define的拷贝有很多份,故宏定义的内存占用要高得多

12、volatile的用法

被定义为volatile的变量可能会被意想不到地改变,编译器不会对volatile变量有关的运算进行编译优化:每次使用该变量必须从内存地址中读取,而不是保存在寄存器中的备份
用到volatile的几种情况
  • 并行设备的硬件寄存器(如状态寄存器)
  • 中断服务子程序会访问到的非自动变量
  • 多线程应用中被几个任务共享的变量

13、常量指针、指针常量、常量引用(没有引用常量)

常量指针即常量的指针,指针所指向的是个常量,可以被赋值为变量的地址,但是不能通过这个指针来修改
指针常量本质是一个常量,指针所指向的值不可以改变,但指向的地址所对应的内容可以变化
(具体参考问题17)

14、变量的作用域(全局变量和局部变量)

  • 全局变量:在所有函数体外部定义的,程序所在部分都可以使用,不受作用域的影响(生命期一直到程序的结束)
  • 局部变量:局限于作用域内,默认为auto关键字修饰,即进入作用域时自动生成,离开作用域时自动消失;
  • 局部变量可以和全局变量重名,在局部变量作用域范围内,全局变量失效,采用的是局部变量的值

15、sizeof和strlen

  • sizeof是一个操作符或关键字,不是一个函数,而strlen是一个函数
  • sizeof返回一个对象或类型所占的内存字节数,不会对其中的数据或指针做运算
  • strlen返回一个字符串的长度,不包括'/0'

16、sizeof(struct)和内存对齐

内存对齐作用:1、移植原因:某些硬件平台只能在某些特定地址处取特定类型的数据;2、性能原因:数据结构(尤其是栈)应尽可能在自然边界上对齐,未对齐内存需要做两次内存访问,对齐内存仅需要一次
struct内存对齐原则:
  • 结构体成员中,第一个成员偏移量是0,排列在后面的成员的当前偏移量必须是当前成员类型的整数倍
  • 结构体本身占用内存大小,应是结构体内最大数据成员的最小整数倍
  • #pragma pack(n)预编译指令,所有成员对齐以n字节为准,不再考虑当前类型和最大结构体内类型
union内存对齐原则:
  • union字节数必须是占用字节数最多的成员的字节数的倍数,而且需要能够容纳其他成员

17、char * const,const char *

const char *ptr指向字符常量的指针,ptr是一个char*类型的常量,所指向的内容不能修改;
char * const ptr指向字符的指针常数,即const指针,不能修改ptr指针,但可以修改该指针指向的内容

18、inline函数

被频繁调用的函数会导致栈空间或栈内存的大量消耗,因此引入inline修饰函数,即内联函数;内联函数将在程序的每个调用点上“内联式地”展开。内联以代码膨胀为代价,省去了函数调用的开销,从而提高函数的执行效率

19、内存四区,变量存储区域(堆/栈)

  • 代码区:.text
  • 全局初始化数据区/静态数据区:.data,明确被初始化的全局变量、静态变量和常量数据,整个生命周期内都可能需要访问
  • 未初始化数据区:.bss,全局未初始化变量
  • 栈区stack:由编译器自动分配释放,存放函数的参数值、局部参数的值等。每当一个函数被调用,该函数返回地址和调用信息,如某些寄存器内容,会被存储到栈区,这个被调用的函数再为它的自动变量和临时变量在栈区上分配空间,即C实现函数递归调用的方法
  • 堆区heap:用于动态内存分配

20、数组名和指针区别

  • 数组名对应的是指向数组首元素地址的指针,但该指针所指的地址不能被改写;
  • 指针是变量指针,所指向的地址可以更改;
  • 对数组名取地址,得到的是数组首元素的地址;对指针取地址,得到的是指针变量所在地址
  • 对数组名使用sizeof,得到的是数组元素个数与元素类型字节数的乘积;而对指针使用sizeof得到的是指针类型的字节数

21、strcpy和memcpy的区别

  • 复制的内容不同:strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等
  • 复制的方法不同:strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度
  • 用途不同:通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

22、递归和循环如何选择

  • 递归算法优点是代码简洁清晰,容易验证正确性;缺点是需要多次数的函数调用,如果调用层数比较深,需要额外增加堆栈处理,会对执行效率有一定影响
  • 循环算法速度快,结构简单;缺点是不能解决所有问题,有些问题不太适用
  • 总结:在求解规模不确定或求解规模明显过大情况下,递归的函数调用开销会很大,因此效率会很低

23、野指针

  • 野指针是指指向内存未知区域或访问受限区域的指针,结果未知
  • 产生原因
    • 指针定义时未被初始化,默认值随机
    • 指向的内存区域被释放时,指针没有置空
    • 指针操作超越变量作用域,如函数返回指向栈内存的指针或引用

24、全局变量和静态变量区别

  • 存储方式上并无区别,都是静态存储方式
  • 非静态全局变量作用域为整个源程序;当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的,而静态全局变量则限制了其作用域,只在定义该变量的源文件内有效

#高频知识点汇总##笔经##C/C++#
全部评论
感谢老哥
3 回复 分享
发布于 2022-02-23 21:27
这是C的吧。几乎没咋提C++的特性
1 回复 分享
发布于 2022-09-05 19:01 北京
感谢老哥!
点赞 回复 分享
发布于 2023-05-31 22:46 广东

相关推荐

09-29 11:19
门头沟学院 Java
点赞 评论 收藏
分享
10-05 23:02
东北大学 Java
我说句实话啊:那时候看三个月培训班视频,随便做个项目背点八股,都能说3 40w是侮辱价
点赞 评论 收藏
分享
61 527 评论
分享
牛客网
牛客企业服务