/const的作用
共同点:
都是C/C++中的关键字
static:
修饰函数局部变量 延长了变量的生命周期 随着程序的结束而结束 但作用域只在定义它的函数体内
修饰全局函数 使函数作用域限制在定义它的源文件内 其他文件无法进行访问
修饰全局变量 使全局变量作用域限制在定义它的源文件内 其他文件无法进行访问
修饰类中成员变量 使类中所有对象共享一个静态成员变量 随着程序的结束而结束
修饰类中成员函数 静态成员函数只能访问类中其他静态成员 没有this指针
const:
修饰常量 使修饰的数据类型的常量不被修改
修饰指针常量 const * int ptr / const int * ptr 指针的指向不能改变 指向的值可以改变
修饰常量指针 int * const ptr 指针指向的值不可以改变 指针的指向可以改变
修饰常成员变量 使其在对象的整个生命周期内保持不变 必须在初始化列表中进行初始化
修饰函数参数 保护参数在函数内部不会被更改
修饰函数返回值 保护返回值不会被更改
修饰常成员函数 该函数不会修改类的任何成员类型 出了mutable修饰的成员变量
2.#define/typedef的区别
#define:
预处理指令 用于文本替换 不受作用域的限制 不进行类型检查
#typedef:
关键字 用于为已存在的数据类型定义新的类型别名 遵循正常的作用域规则 进行类型检查
告诉编译器 被其修饰的变量的值可能会被程序以外的因素(如操作系统、硬件等)所改变 因此每次使用该变量时都需要从内存中重新读取 而不能使用保存在寄存器中的缓存值
/strlen
sizeof:
用于计算变量或数据类型所占用内存的字节数(包含\0) 是操作符 在预处理阶段进行 不对括号内的值进行运算
strlen:
用于计算字符串数组的长度(不包含\0) 是函数 在程序运行阶段进行 会对括号内的值进行运算
编译四个阶段
预处理 展开头文件 进行宏替换 删除注释
编译 有错报错 没错继续运行 生成不可执行二进制文件
汇编 生成可执行二进制
链接 链接库函数 动态库或者静态库
6.数组/链表
数组:
存储:连续存储 占用固定的空间 资源利用率低 容易造成资源浪费 或 不足
访问:可以通过下标直接访问 时间复杂度为O(1)
插入/删除:需要移动大量数据 时间复杂度为O(n)
链表:
存储:随机存储 按需分配 资源利用率高 但每个节点需要额外的存储空间
访问:每次访问需要从头节点开始遍历 时间复杂度为O(n)
插入/删除:可以从任意节点插入 只需修改相关指针 时间复杂度为O(1)
7.指针的理解
指针 内存地址
指针变量 存储内存地址的变量
-> 间接访问和操作所指向的内存空间的数据
->> 实现内存的动态分配
->> 实现复杂的数据结构
->> 作为函数参数进行高校的参数传递
使用时
-> 初始化 防止指向任意内存位置
-> 不使用时要进行内存释放
-> 注意指针的运算以及解引用
避免野指针
-> 初始化指针
-> 释放内存后置空
-> 注意防止下标越界
8.结构体和公用体的区别
结构体:
结构体中每个成员都有自己独立的存储单元 在内存中一次存放 结构体的大小是所有成员和 但是需考虑字节对齐
共用体:
共用体成员共享同一段的内存空间 共用体的大小等于最大成员的大小 某一个时刻只能存储一个成员的值
9.结构体字节对齐
为了提高访问效率 结构体的第一个成员的其实地址是结构体起始地址 偏移量从0的位置开始存储 每个成员相对于结构体起始地址的偏移量必须是该成员数据类型大小的整数倍
10.堆和栈的区别
共同点:
都是用于进行内存管理的
堆区:
系统自动分配和释放 效率较高 拥有固定的大小 内存分配时连续的 遵循先进后出原则
栈区:
由程序员手动分配和释放 效率较低 取决于计算机中物理内存的大小 分配时看你导致内存碎片
malloc calloc realloc free/ new delete
11.全局变量和局部变量的区别
全局变量:
定义在函数体外 存储在数据段中 没进行初始化时候 他的值默认为0 作用域为整个程序 随着程序的结束而结束 所有函数都可以进行访问
局部变量:
定义在函数体内 没进行初始化时 会是随机值 需要显示的进行初始化 作用域为整个函数 随着函数的结束而结束 只能在函数内被访问
和memcpy的区别
strcpy:
专门用于字符串复制 遇到'\0'停止
memcpy:
专门用于内存区域复制 可以复制任意类型的数据 按照指定的字节数num进行复制 不关注数据内容 遇到'\0'不会停止
13.什么是段错误 怎么解决段错误
段错误:
程序试图访问它无权访问的内存区域 比如 访问空指针 访问数组下标越界 栈溢出 访问已释放内存等
可以通过代码审查 添加错误处理代码 使用调试工具等 解决段错误
14.什么是内存泄漏 什么是野指针
内存泄漏:
程序中已动态分配的堆内存由于某种原因程序未释放或无法释放 造成系统内存浪费 导致程序运行速度减慢甚至系统崩溃
产生原因:
忘记释放内存(C/C++) 循环引用(Java/JavaScript)
野指针:
程序指向一块已经被释放或者已经无效的内存地址的指针 会导致不可预测的行为 如程序崩溃 数据损坏 或者尝试奇怪的运行结果
产生原因:
指针变量未初始化 针指向的内存被释放后未置空 局部指针变量超出作用域
解决方法:
初始化指针为NULL/nullptr 对不再使用的指针及时置空 检查指针是否为空
15.数组指针和指针数组的区别
数组指针:
int (*ptr) [5]
是一个指针 指向一个数组 指针存储的是数组的首地址 在内存中只占用一个指针大小的空间 访问数组元素时 需要通过指针解引用到数组 再更具偏移量来访问具体元素
指针数组:
int * ptr[5]
是一个数组 这个数组的每个元素都是指针 占用的内存空间是每个指针大小乘以数组元素个数
16.指针和引用的区别
指针:
本身是一个变量 存储的是另一个变量的内存地址 64位系统占8字节内存 可以被赋值为NULL/nullptr 可以重新赋值 改变指针的指向 指针作为参数传递时 传递的是变量的地址 函数可以通过这个地址改变原变量的值
引用:
引用时一个已存在变量的别名 不占用内存 一旦绑定后 不能再重新赋值 值是唯一的 引用作为函数参数是 形参是实参的别名 对形参操作就是对实参的操作
17.结构体和类的区别
结构体:
默认访问级别和默认继承方式为public 通常用于表示简单的数据集合
类:
是由结构体演化而来 默认访问级别和默认继承方式为private 更侧重于封装数据和相关的方法
和C++的区别
C
C是面向过程的编程语言 主要关注算法和数据结构的实现 不支持函数的重载和运算符的重载 没有引用的概念 没有异常处理机制 没有命名空间的概念
C++
C++是面向对象的编程语言 在C的基础上发展而来 有类和对象的概念 有封装 继承 多态的思想 支持函数的重载和运算符的重载 有引用的概念 提供了try-catch-throw异常处理机制 引用命名空间 解决命名的污染问题
19.什么是构造函数 析构函数
构造函数:
构造函数时类中的特殊成员类 名字与类名相同 没有返回值类型 主要用于创建对象时进行初始化操作 分为无参构造和有参构造 有参构造可以使用初始化列表进行 他们之间构成重载关系 当类中有const修饰的成员变量和引用成员时必须使用初始化列表完成
析构函数:
/free new/delete的区别
共同点:
在堆区进行内存的申请与释放 用于动态内存管理 多次进行释放内存会导致段错误
malloc/free:
c语言中的函数 用于动态分配指定字节数的内存空间 malloc是不安全的 不知道所分配的内存的数据类型 free只是简单地释放内存 不会调用任何析构函数
new/delete:
C++中的操作符 不需要指定字节数系统自动计算 new是安全的 知道所要分配的内存的数据类型 delete释放内存之前会调用对象的析构函数 进行资源清理等操作 delete释放数组时候 需要加[]
21.虚函数的作用
22.对多态的理解 多态的原理
理解:
多态是面向对象编程中的一个核心概念 实现对用一操作有多种形式 提高代码的可扩展性和可维护性 符合开闭原则
原理:
多态主要通过虚函数和动态绑定实现 一个类中有虚函数时 编译器会为这个类创建一个虚函数表
23.为什么在继承中将析构函数定义为虚析构函数
当通过基类指针删除派生类的对象时 如果析构函数不是虚函数 那么只会调用基类的析构函数 而派生类的析构函数不会被调用 这可能导致派生类中分配的资源 没有被正确的释放 造成内存泄漏
24.构造函数可以定义成虚函数么
构造函数不能定义为虚函数 在创建对象的时候需要先分配内存空间 然后调用构造函数来初始化对象 在调用构造函数前 对象还没有完全形成 此时不存在虚函数表 就无法实现虚函数的动态绑定机制