文章目录
- C/C++
- C++11新特性
- 宏定义交换两个不确定类型的变量
- extern关键字的作用
- 在C++ 程序中调用被 C编译器编译后的函数,为什么被包含 extern "C"?
- 头文件卫士的作用
- C中全局变量 局部变量能同名吗? 如果同名怎么访问全局变量
- sizeof与strlen的区别
- struct的作用
- 访问类的私有成员时,友元类和该类的get和set函数哪一个更好
- socketTCP协议服务器端的编程模型,用到哪些函数
- select函数
- 前置声明,解决头文件重复包含的问题
- 在抽象类中,如果想增加一个接口,把该接口定义在所有接口之前会有什么问题
- C++中三种继承方式的子类对父类三种成员的访问权限
- 虚函数和纯虚函数的区别
- C和C++中static有什么作用,分别描述一下
- printf是从左到右打印还是从右到左打印
- vector自动扩容是如何实现的
- if(n\==10)和if(10\==n)哪一种更好,为什么
- 宏定义如何换行,如何连接字符
- float x与“零值”进行比较,为什么使用了0.000001而不是使用0(整型)
- 中数组名a和&a的区别
- 多态的原理
- 你知道排序算法有哪些吗
- 什么是多态?
- 有了malloc和free为什么用new/delete(我写的是这两个的区别)
- id[sizeof(unsigned long)]这种写法是正确的吗?为什么?
- 指针和引用的区别
- C++中有哪些传参方法
- C++中struct和class区别
- 描述一下重载,隐藏,覆盖
- 有用过二级指针吗?对二级指针有什么理解?
- const和static关键字怎么加,加上去有什么作用? 全局函数加上static有什么用?
- c语言中字符串拷贝函数除了strcpy还有什么拷贝函数?
- 在C++中指针与引用有什么相同点和不同点?
- 面向对象的三个基本特征是什么,并简单叙述之。
- 内存对齐的规则
- 内存对齐的优点和与内存不对齐的区别
- const与指针
- 什么是指针函数与函数指针
- volatile的用法
- char arr[1024\*1024\*4];是否合理合法,如何修改
- ++i/i++的区别 是否是原子操作
- 强制类型转换
- 构造和析构函数可以是虚函数吗
- 数据结构和算法
- UC
- Qt
- 网络
- 数据库
C/C++
C++11新特性
右值引用: 调用移动构造函数,减少不必要的拷贝
- 资源窃取
- std::move() 语义转换,将参数转换为右值引用类型
- 利用即将结束生命周期的右值的资源,从而避免临时对象的销毁和拷贝
- 完美转发
- std::forward() 保持参数的左右值属性,
- 模板编程时将参数转发给其他函数
智能指针
- shared_ptr
- 内部维护引用计数,到0自动销毁
- 可复制和赋值
- 可自定义销毁方法
- unique_ptr
- 无法赋值和复制,无引用计数,用于资源独占
- 生命周期结束自动调用delete释放
- 支持
std::move()
移动语义转移资源- 可自定义销毁方法
- weak_ptr
- 和shared_ptr结合使用,解决shared_ptr相互引用问题
lambda表达式
- 匿名函数
- 在做安卓开发的时候经常用java里的lambda表达式,java中和c++的区别是java无捕获变量的操作,而是直接可以访问所有外部变量
nullptr
- 解决重载函数匹配问题
宏定义交换两个不确定类型的变量
#define swap(a,b) {a = a ^ b; b = b ^ a; a = a ^ b;}
extern关键字的作用
- 声明在其他文件中定义的外部变量或函数
- C++中
extern C
告诉编译器使用C的标准编译代码
在C++ 程序中调用被 C编译器编译后的函数,为什么被包含 extern “C”?
因为c++中调用同名函数要考虑函数重载,要对函数类型进行声明
头文件卫士的作用
- 防止头文件重复包含
C中全局变量 局部变量能同名吗? 如果同名怎么访问全局变量
可以同名,局部变量会屏蔽全局变量,C中无法再在该作用域访问全局变量,C++可以使用域限定符访问全局变量
sizeof与strlen的区别
sizeof
计算的是实际所占的内存空间的大小strlen
计算的是字符串的长度,到'\0'
截止,且不包括
struct的作用
- 把不同的变量整合到一起,方便数据的管理
- 使多个数据的存储位置连续,把多个数据封装成一个数据包,方便写入文件和网络传送
访问类的私有成员时,友元类和该类的get和set函数哪一个更好
get和set函数,因为使用友元会破坏类的封装性,安全性
socketTCP协议服务器端的编程模型,用到哪些函数
socket,bind,listen,acctep,read,write,recv,send
select函数
前置声明,解决头文件重复包含的问题
- 前置声明只是告诉编译器这是一个类型,但是无法得知类型的大小,成员,是一种不完整的声明
- 如果一个文件中只需要用到另一个文件中的类类型的指针或应用,或者用于声明该类型作为形参或返回值类型,只需对该类型进行前置声明,而不需要include,提高编译效率
在抽象类中,如果想增加一个接口,把该接口定义在所有接口之前会有什么问题
C++中三种继承方式的子类对父类三种成员的访问权限
父类成员 内部 子类 外部 友元 子类对象(public) 子类对象(protected) 子类对象(private) public ✔ ✔ ✔ ✔ ✔ ✘ ✘ protected ✔ ✔ ✘ ✔ ✘ ✘ ✘ private ✔ ✘ ✘ ✔ ✘ ✘ ✘
父类成员 public继承的子类 protected继承的子类 private继承的子类 public public protected private protected protected protected private private 不可直接访问 不可直接访问 不可直接访问
虚函数和纯虚函数的区别
- 含有纯虚函数的类被称为抽象类,一般用来定义接口,无法实例化
- 虚函数必须实现,可以直接使用,或被子类覆盖,而纯虚函数在基类中只有声明没有定义,必须在子类中实现才能使用
- 都可在子类中被重载,以多态形式被调用,是一种运行时的多态(重载:编译时多态)
C和C++中static有什么作用,分别描述一下
- C++中static修饰成员变量: 使变量属于整个类而不是某个对象
- C++中static修饰成员函数: 函数失去this指针,只能访问static成员,使函数属于整个类
- 修饰局部变量: 作用域不变,延长生命周期为全局(整个程序),只初始化一次(默认初始化为0)
- 修饰函数: 限制作用域为本文件
printf是从左到右打印还是从右到左打印
从右往左计算,从左往右输出
vector自动扩容是如何实现的
内部预置容量大于等于元素数量
当前容器容量无法容纳元素时重新申请更大内存并复制
不同编译器实现扩容方式不同,GCC按2倍扩容
capacity()查看当前可容纳的元素数量
reserve()设置预留元素个数
if(n==10)和if(10==n)哪一种更好,为什么
第二种更好,因为第一种如果少写了一个’=’,则if语句括号中就变成了赋值语句n=10,而这样写能通过编译,编译器无法检测到错误,用第二种写法,若是少些一个’=’,则if语句中就变成了10=n,在C/C++的语法中并没有这样的语法存在,因此无法通过编译,编译器可以直接检测到错误。用第二种写法可以有效降低bug的出现率,是一种良好的编程风格
宏定义如何换行,如何连接字符
反斜杠
\
换行
##
连接字符(串)
float x与“零值”进行比较,为什么使用了0.000001而不是使用0(整型)
中数组名a和&a的区别
- a表示数组中第一个元素的地址,
- &a表示整个数组的地址,两者在数值上相同,但对两者+1运算后a+1指向数组第二个元素,&a+1指向整个数组末尾的下一个地址(下一个数组的地址)
多态的原理
在C++类中,一旦成员函数中有虚函数,这个类中就会多一个虚函数表指针,这个指针指向一个虚函数表,表里记录了这个类中所有的虚函数,当这个类被继承,它的子类中也会有一个虚函数表(不管子类中有没有虚函数),如果子类的成员函数中有函数签名与父类的虚函数一样就会用子类中的函数替换它,在虚函数表中的位置,这样就达到了覆盖的效果。当通过类指针或应用调用函数时,会根据对象中实际的虚函数表记录来调用函数,这样就达到了多态的效果
你知道排序算法有哪些吗
冒泡,选择,插入,快速,归并,堆
什么是多态?
- 运行时多态: 当子类覆盖了父类的虚函数时,通过父类指针指向子类对象时,调用虚函数,会根据具体的对象是谁来决定执行谁的函数
- 编译时多态: 重载
有了malloc和free为什么用new/delete(我写的是这两个的区别)
malloc/free new/delete 会自动计算所需内存的大小分配/释放 自动调用类的构造和析构函数 malloc返回void* new返回具备类型的指针 new[]会在地址的前4字节保存数组长度
id[sizeof(unsigned long)]这种写法是正确的吗?为什么?
正确,32位系统中
sizeof(unsigned long)
的值为4,个人认为正确与否取决于编程者的意图
指针和引用的区别
指针 引用 是一个变量 是变量的一个别名 定义时可不初始化 必须初始化且不可NULL 初始化后可变 初始化后不可变 可以有多级 只有一级 sizeof得到指针变量的大小 得到指数据的大小 有const 没有const
C++中有哪些传参方法
指针,引用,值
C++中struct和class区别
struct也能包含成员函数,继承,多态;class和struct也可以相互继承
struct class 默认成员 public private 默认对父类的继承方式 public private 定义时赋值 可用{}(但是如果加入构造或虚函数就不性) 不可以用{}
描述一下重载,隐藏,覆盖
- 重载:在同一作用域下,函数名相同,参数列表不同的函数构成重载
- 隐藏:不同作用域下,函数名相同,参数列表不同
- 覆盖:子类的成员函数与父类的虚函数函数名(包括常函数),返回值(可以是父子类)及参数列表(包括const属性)都相同,构成覆盖
- 子类与父类同名的函数,如果没有构成覆盖,必构成隐藏
有用过二级指针吗?对二级指针有什么理解?
二级指针指向的是一个指针,是指针的指针
const和static关键字怎么加,加上去有什么作用? 全局函数加上static有什么用?
- const: 保护变量不被修改
- 常成员函数可以保护成员变量不被修改
- static: 限制作用域,改变存储位置
- static 全局变量/函数:只能在当前文件使用
- static 局部/块变量:栈->BSS
- static 局部/块变量(初始化):栈->DATA
c语言中字符串拷贝函数除了strcpy还有什么拷贝函数?
void *memcpy(void *dest, const void *src, size_t n);
int sprintf( char *buffer, const char *format, ... );
char *strncpy(char *dest, const char *src, size_t n);
在C++中指针与引用有什么相同点和不同点?
- 相同点: 可以优化传参效率,可以跨函数共享变量
- 不同点
- 指针是一种数据类型,而引用只是一个别名
- 引用在定义时必须初始化且不能为空不能改变,指针可以不初始化可以为空可改变
- 对指针指向的对象操作需要解引用,引用不需要
- 指针有常量指针和指针常量,引用只有常量引用
面向对象的三个基本特征是什么,并简单叙述之。
- 封装:将客观事物抽象封装成一个类
- 继承:子类能继承父类,即子类能成为父类的拓展形态
- 多态:对应不同的状况,父类能以不同的子类形态运行
内存对齐的规则
- 对齐:pragma pack 、自身size 取小的
- 补齐:pargma pack 、最大类型size 取小的
- 默认pragma pack(8)包括32位
内存对齐的优点和与内存不对齐的区别
- 提升cpu访问数据的效率
- 平台(硬件)兼容性
const与指针
const int *p;
常量指针
int const *p;
常量指针
int* const p;
指针常量
const* int p;
错误写法
什么是指针函数与函数指针
- 指针函数:
int* func();
返回值是指针的函数- 函数指针:
int (*p)();
指向函数的指针,指向函数的入口地址
volatile的用法
volatile 关键字是一种类型修饰符,告诉编译器该变量表示可以被某些编译器未知的因素更改,防止编译器对齐优化,每次访问都从其内存中读取
char arr[1024*1024*4];是否合理合法,如何修改
- 不合理,不一定合法: 可能会栈溢出,取决于平台,一般linux的栈空间是8m(未验证,内存是分页的4k)
- 使用堆内存,使用指针数组
++i/i++的区别 是否是原子操作
- ++i 是先自增,再赋值
- i++ 是先赋值,再自增
- 都不是原子操作
强制类型转换
构造和析构函数可以是虚函数吗
- 构造函数不可以是虚函数
- 析构函数可以是虚函数
数据结构和算法
set和map的区别是什么
set中的数据会自动排序,map存储的是键值对键不能重复
有没有使用过迭代器,怎么用
stl知道哪些
list,vector,map,stack,queue,set
map和vector的区别
- map
- 红黑树实现的平衡查找二叉树,内存中不连续
- 键值对,key不重复
- vector
- 连续存储
- 插入删除消耗较大
map的底层数据结构
红黑树
Vector与List的区别
- Vector为存储的对象分配一块连续的内存,因此随机存取更高效
- List中对象是离散存储的,插入和删除效率更高
排序算法的复杂度
算法 平均时间 最好 最坏 空间 稳定性 冒泡 O(n^2) O(n) O(n^2) O(1) ✔ 直接插入 O(n^2) O(n) O(n^2) O(1) ✔ 直接选择 O(n^2) O(n^2) O(n^2) O(1) ❌ 快速 O(nlog_2n) O(nlog_2n) O(n^2) ❌ 堆 O(nlog_2n) O(nlog_2n) O(nlog_2n) O(1) ❌ 归并 O(nlog_2n) O(nlog_2n) O(nlog_2n) ✔
知道哪些树的类型
普通树, 二叉树, 完全二叉树, 满二叉树, 查找二叉树, 哈夫曼树
UC
用一段代码来判断机器是16位的还是32位的?
int* a;
switch(sizeof(a)){
case 2: printf("16\n"); break;
case 4: printf("32\n"); break;
}
编译过程
- 预处理
gcc -E code.c 会把预处理的结果显示在屏幕上
gcc -E code.c -o code.i 把预处理的结果保存再.i文件中- 汇编
gcc -S code.i 会生成以.s结尾的汇编文件- 编译
gcc -c code.s 会生成以.o结尾的目标文件(二进制指令)- 链接
gcc code.o 把若干个.o文件合并成一个可执行文件elf
使用过gdb吗
- 之前学习的时候通过命令的形式使用过gdb(
run, next, list, break, continue, print, quit
),后来基本都用IDE的调试工具了- ubuntu下使用gdb不会产生core文件,要用
ulimit -c unlimited
修改设置
用户态和内核态是什么,什么时候进入内核态
- 运行低特权级的用户代码时是用户态
- 运行高特权级的内核代码时是内核态,进行系统调用的时候会进入内核态
- 保护系统资源不被用户程序随意访问和修改
进程和线程之间哪些数据是共享的
- 线程除栈外其他资源都共享: 代码段,数据段,bss段,堆(没有栈),环境变量表,命令行参数,文件描述符,信号处理函数,工作目录,用户ID,组ID等资源
- 子进程除共享代码段外复制父进程其他: 数据段,bss段,堆,栈,IO流(共享文件指针和文件描述符),缓冲区拷贝
如何保证对共享资源的操作为原子操作?
- mutex互斥锁
- C++11原子类型
静态库和共享库的区别
- 静态库: 程序编译的时候连接到目标代码中,运行时不需要,更新库需要重新编译
- 动态库: 程序运行时被载入,运行时需要库存在
- 对比
- 静态库: 浪费内存,执行速度快,集成度高
- 动态库: 更节省适合算法库和功能库,方便更新和升级
进程间通信有哪几种方式?各有什么特点?
- 简单进程通信:命令行参数,环境变量表,信号,文件
- 传统:有名管道(fifo),无名管道(pipe,亲缘进程间)
- XSI:共享内存(最快),消息队列,信号量(控制对共享资源的访问)
- 网络 socket(不同设备进程之间)
知道共享内存吗?说一下原理?有用过吗?
- 内核中开辟的一块由IPC对象管理的内存,进程A和进程B将自己的虚拟地址与其映射
- 特点:不需要复制数据,是最快的一种进程间通信;需要考虑同步问题(信号量)
- 编程模型
进程A 进程B 生成IPC键值 ftok 生成IPC键值 ftok 创建IPC对象(共享内存) shmget 创建IPC对象(共享内存) shmget 映射共享内存 shmat 映射共享内存 shmat 使用共享内存 *ptr 使用共享内存 *ptr 取消映射 shmdt 取消映射 shmdt 删除共享内存 shmctl …
用过多线程吗?怎么样防止多个线程同时访问?
互斥量(互斥锁mutex),条件变量(cond),信号量
一般互斥锁和条件变量一起使用互斥锁用来锁定资源,条件变量用来阻塞线程
死锁产生的条件
- 互斥
- 不可剥夺
- 请求保持
- 环路等待
避免死锁的方法
构成死锁的四个条件只要破坏其中一个就构不成死锁,死锁一旦形成,就无法消除,因此最好的方法就是避免产生死锁
- 破坏互斥条件,让资源能够共享,但缺点是不通用,因为有些资源不能共享,如打印机
- 破坏请求并保持条件,采用预先分配的方法,在进程运行前一次性申请好它所需要的所有资源,缺点是浪费资源
- 破坏不可剥夺条件,对已占用资源的线程发送取消请求,但实现比较复杂,而且还会破破坏业务逻辑.
- 破破坏循环等待条件,为每一个资源进行编号,采用顺序的资源分配方法,规定每个线程必须按照递增的顺序请求资源,缺点是编号必须相对稳定,而且增加新的资源时会比较麻烦,而且有些特殊的业务逻辑不能完全按照指定的顺序分配资源
为什么用到线程
同进程一样可以进行并行运算,避免阻塞,提高CPU利用率,但是进程间的切换非常耗费资源和时间,为了进一步提高操作系统的并发性,引入了线程,进程是资源分配的基本单位,线程是系统调度的基本单位
linux下的命名知道哪些
ls,cd,mkdir,cp,rm,rmdir,ipconfig,ps,strace,vim,g++,gcc,sudo,su,apt-get,cat,vi,reboot,grep,find...
打开文件的函数是什么?
- UNIX系统调用open
- 标准C库函数fopen
进程映像:
- 进程是正在执行的程序
- 进程映像就是进程的资源在内存的分布情况,地址从低到高:
代码段/只读段 二进制指令/字符串字面值/具有const属性且被初始化过的全局变量/静态变量 数据段 被初始化过的全局变量/静态变量 bss段 没有被初始化过的全局变量/静态变量,进程加载成功清理为0 堆 动态的分配,管理,需要程序员手动操作 栈 非静态的局部变量,包括函数的参数,返回值(从高地址向低地址使用,和堆内存存在一段空隙,防止相互影响) 命令行参和环境变量表 命令行参数/环境变量表
栈和堆的空间区别,为什么用堆?
- 栈的内存空间较小且固定,Linux下8/10m,堆的内存空间可以自己扩展,理论上是虚拟内存用户空间的大小(3G)
- 栈中内存空间是编译器自动分配,堆中内存空间是程序员手动申请分配的c中函数是malloc/free,c++中是new/delete
- 栈中内存访问速度也比堆中内存访问速度较快
你会遇到内存泄漏吗?
- 遇到过,在avt项目中遇到过
- 大部分都通过我们内部的工具检测出来了,我们内部对malloc和free封装成了一个工具类memtrace,可以检测到申请后没有被释放的内存并打印出文件名,行号,内存大小
- 还遇到过一次显存的泄露,在GL线程里,无法用工具检测到,通过使用高通的工具snap dragon profile发现一张纹理图大量重复,最终定位到代码中一个textureID没有被释放
用什么分配内存,重复释放是什么结果
new,malloc分配内存,重复释放会造成程序崩溃,可使用shared_ptr解决(自动释放,无须显式调用free)
两个线程访问一个变量有什么问题
一个变量在同一时间段只能被一个线程访问,两个线程会竞争访问变量的顺序
而且如果两个线程访问这个变量的时候是不只是读取变量的时候,就会出现数据不一致的问题,产生脏数据
有哪几种锁
互斥量,线程的信号量,条件变量
Makefile
高速缓冲区的作用
- 缓冲:io设备和内存之间速度不匹配
- 减少io设备的频繁读写,减少损耗
Qt
Qt中signal和slot
- 信号和槽通常用于对象间的通信,类似于回调和软中断的概念
类型安全:只有参数匹配的信号与槽才可以连接成功(信号的参数可以更多,槽会忽略多余的参数)。- 线程安全:通过借助QT自已的事件机制,信号槽支持跨线程并且可以保证线程安全。
- 松耦合:信号不关心有哪些或者多少个对象与之连接;槽不关心自己连接了哪些对象的哪些信号。这些都不会影响何时发出信号或者信号如何处理。信号与插槽建立了强大的组件编程机制。
- 信号与槽是多对多的关系:一个信号可以连接多个槽,一个槽也可以用来接收多个信号。
- 使用这套机制,类需要继承QObject并在类中声明Q_OBJECT
网络
三次握手和四次挥手
A B 3次握手 1 听得到吗(SYN)-> B知道A可到B 2 A知道A能到B,B能到A <-(SYN+ACK)我听得到,你呢 3 我听得到(ACK)-> B知道了B也能到A 4次挥手 1 发送关闭请求-> 2 <-发送请求响应 检查是否还有未发送数据 3 <-可以关闭 4 发送关闭信息->
三次握手(服务器没收到最后一次ACK会怎么样)
服务器会重发上一次的SYN+ACK
C++中的socket吗?说一下服务端跟客户端的编程模型?
- TCP
Server Client 创建socket套接字 创建socket套接字 准备地址(sockaddr_in,本机地址) 准备地址(服务端地址) 绑定(bind) … 监听(listen) … 等待连接(accept,fork) 连接(connect) 接收请求(read/recv) 发送请求(write/send) 响应请求(write/send) 接收响应(read/recv) 关闭(close) 关闭(close)
- UDP
Server Client 创建套接字(socket) 创建套接字(socket) 准备地址(本机地址sockaddr_in) 准备地址(目标机地址> sockaddr_in) 绑定(bind(sockfd+addr)) … 接收请求(recvfrom) 发送请求(sendto) 响应请求(sendto) 接收响应(recvfrom) 关闭套接字(close) 关闭套接字(close)
知道TCP/IP模型吗?你比较熟悉那一层?
OSI TCP/IP 数据单位 应用层 应用层 主要是为用户提供针对性的服务,HTTP,SMTP,FTP,SENMP,TELNET,DNS 报文 表示层 ⬆ 会话层 ⬆ 传输层 传输层 机器之间建立用于会话的端到端连接(用于数据传输,TCP,UDP 传输协议分组 网络层 网络层 选择,流量控制,网络拥塞,IP,ICMP,ARP,RIP ip数据报 数据链路层 ⬇ VLAN,MAC 帧 物理层 网络接口层 负责通信网络收发数据包IEEE802.3 比特
Socket网络通信中阻塞与不阻塞的区别?
读
- 阻塞: 接收缓冲区无数据时一直阻塞,直到读取到数据返回
- 非阻塞: 接收缓冲区无论有无数据都会立即返回
写
等待连接
建立连接
写出TCP协议与UDP协议特点。
TCP:
- TCP是面向连接的传输层协议;
- 每一条TCP连接只能有两个端点(即两个套接字),只能是点对点的;
- TCP提供可靠的传输服务。传送的数据无差错、不丢失、不重复、按序到达;
- TCP提供全双工通信。允许通信双方的应用进程在任何时候都可以发送数据,因为两端都设有发送缓存和接受缓存;
- 面向字节流。虽然应用程序与TCP交互是一次一个大小不等的数据块,但TCP把这些数据看成一连串无结构的字节流,它不保证接收方收到的数据块和发送方发送的数据块具有对应大小关系,例如,发送方应用程序交给发送方的TCP10个数据块,但就受访的TCP可能只用了4个数据块久保收到的字节流交付给上层的应用程序,但字节流完全一样。
UDP:
- UDP是无连接的传输层协议;
- UDP使用尽最大努力交付,不保证可靠交付;
- UDP是面向报文的,对应用层交下来的报文,不合并,不拆分,保留原报文的边界;
- UDP没有拥塞控制,因此即使网络出现拥塞也不会降低发送速率;
- UDP支持一对一 一对多 多对多的交互通信;
- UDP的首部开销小,只有8字节.
TCP UDP 可靠传输 不可靠传输 面向连接 无连接 传输数据有序 不保证数据的有序性 不保存数据边界 保留数据边界 传输速度相对较慢 相对较快 有流量控制和拥塞控制 没有 重量级协议 是轻量级协议 首部较长20字节 首部较短8字节
TCP拥有哪些机制?
- 确认重传
- 数据校验
- 流量控制
- 拥塞控制
- 数据合理分片和排序
TCP拥塞控制机制
- 慢启动(指数上升)
- 拥塞避免(线性上升)
- 快重传
- 快恢复
ping使用了哪个协议
ICMP