1,堆内存与栈内存得区别?
堆内存是由程序员手动管理的一段内存,使用相对复杂,但内存足够大。栈内存是存放局部变量
和块变量由操作系统管理的一段内存,会在程序运行过程中自动申请、释放内存,使用简单,但
是内存小。由于栈内存由操作系统自动分配,那么一般只会出现内存越界,但堆内存由程序员操
作,则会出现内存泄漏,内存碎片,内存越界等一系列安全隐患
2,使用堆内存越界的后果?
内存越界使用,这样的错误引起的问题存在极大的不确定性,有时大,有时小,有时可能不会对
程序的运行产生影响,正是这种不易重现的错误,才是最致命的,一旦出错破坏性极大
1,破坏了堆中的内存维护信息,特别是动态分配的内存块的维护信息,因为操作系统在分配和释
放内存块时需要访问该数据信息,一旦该数据被破坏,就会影响之后对内存的申请、释放。
2,破坏了程序自己的其他对象的内存空间,这种破坏会影响程序执行的不正确性,产生段错误
3,破坏了空闲内存块,很幸运,这样不会产生什么问题,但谁知道什么时候不幸会降临
3,什么是内存泄漏?如何定位内存泄漏?
由于业务逻辑出错或者粗心大意导致使用完毕的堆内存没有正常释放,当再次需要时又重新申请,
又没有释放长时间积累下导致可用的内存越来越少,系统就会越来越慢甚至奔溃。
1,查看内存的使用情况
windows 任务管理器
Linux ps-aux命令
GDB查看进程中的内存分布情况
2,借助代码分析工具 mtrace,检查malloc和free有没有成对出现
3,封装malloc 和free,记录申请,释放的信息到日志文件中
4,for while do while 循环的区别:
for循环一般指定一个循环变量,来让代码反复执行
while循环是for循环的精简版,一般用来执行不知道精确次数的循环
do while 与while相似,但do while 至少执行一次
6,#define 与 typedef的区别?
#define INT int
typedef int INT;
INT num;
如果是普通类型,他们的功能上没有任何区别
#define INIP int*
typedef int* INIPP;
INIP P1,P2,P3; //只有p1使指针类型
INIPP p1,p2,p3;//三个都是指针类型
具体区别
1,#define之后不带分号,typedef之后带分号
2,#define可以使用其他类型说明符对宏类型名进行扩展,而 typedef 不能这样做
3,在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证
7,宏函数与普通函数的区别?
宏函数:带参数的宏替换,只是代码的替换,只是使用使用时像函数,不是真正的函数
函数:是一段具有某项功能的代码的集合,会被翻译成二进制指令,存储在代码段,函数名就是
函数的首地址,有独立的栈内存、命名空间。
函数: 返回值 类型检查 安全 内存申请、释放 跳转不易冗余
宏函数:运算结果 通用 相较危险 替换 速度快 代码会冗余
调用频繁、内容简单的功能适合写成宏函数
8,在定义常量时 const 与 #define 有什么区别?
const 定义的常数是变量 也带类型, #define 定义的只是个常数 不带类型
define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用
define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,要进行判断
define可以用来防止头文件重复引用,而const不能
9,顺序栈和链式栈的区别?
顺序栈就像数组一样,在内存中是一段连续的内存,在编译时就需要我们确定好数组大小
对内存的使用率不高,这样的方式无法避免数据超出容量的溢出问题,以及一旦申请好内存的
大小其空位子就不能给其他数据使用,但是支持随机访问,访问、查找效率告,复杂度小。
而对于链式栈来说,其在内存中大多数时候是不连续的,是一种动态内存申请,所以可以
在内存很小时申请,并且在某一个数据不用时把内存返还给系统,插入删除时简单,但是在排序
等需要全部成员参与的运算中,难度就大了起来。
10,ftp服务器如何判断断点续传
1、获取服务器、客户端同名文件的大小 sever_size
2、获取服务器、客户端同名文件的时间戳
3、判断客户端同名文件与服务器同名文件的时间戳一致,并且大小不一样,则该文件需要续传
4、当3满足时,开始断点续传
客户端文件从sever_size处开始读
向服务器发送 REST sever_size 则服务器从该位置开始写
STOR 上传文件
11,进程与线程的区别
1,一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
2,资源分配给进程,同一进程的所有线程共享该进程的所有资源。
3,处理机分给线程,即真正在处理机上运行的是线程。
4,线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
具体区别
1,调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
2,并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
3,拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
4,系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
12,TCP服务端的编程模型有哪几种?以及它们的优缺点?
大致上分为3种
1,
//创建socket
//准备通信地址
//绑定
//监听
for(;;)
{
//等待连接
//接收客户端的请求
//响应请求
}
优点:实用简单,资源耗费少
缺点:一次只能服务一个客户端,服务效率低
2,
//创建socket
//准备要绑定的结构体addr
//绑定
//监听
for(;;)
{
//等待连接
//创建子进程服务
if(0 == fork())
{
//接收客户端的请求
//响应请求
//关闭socket
}
}
优点:解决了一次只能服务一个客户端的问题
缺点:因为产生多个进程,进程间的切换较为耗时
3,
使用select,pselect,poll,epoll 等函数 实现的多路复用服务器
一个进程可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的读写操作。
优点:不需要频繁地创建、销毁进程/线程,从而达到节约内存资源、时间资源,也能避免进程之间的竞争、等待
缺点:单个客户端的任务不能耗时太长,否则其他客户端就会感知到卡顿
13,随着客户端的连接和退出越来越频繁,服务端都需要频繁地创建、销毁线程,该过程会比较耗时,如何解决?
使用线程池,使多个线程在服务器开启时就启动,并进入睡眠状态,客户端访问时,单个线程进行服务,
其它线程继续等待,若线程被用光,客户端则先进行等待,存在空闲线程再进入服务,单个线程服务完
成后不需要销毁,重新进入睡眠状态,从而节省服务器频繁创建、销毁线程的时间。
14,为什么tcp建立连接需要三次握手,断开连接需要四次挥手
三次握手的目的,是为了防止A端已经失效的连接请求突然又传到B端,被误认为是A端再次发出的一个新的连接请求,如果B端这时又再次向A发出确认报文,表示同意建立连接,就会产生错误
第一次是A端向B端发送请求,如果是只有一次握手的话,A端不知道B端是不是收到了这个请求
第二次是B端确认收到A端请求,如果只有两次的话,B端不确定A端是否收到了确认消息,这个确认消息有可能会在半路丢了
第三次是A端确认收到了B的确认消息,A和B双方都是通的,然后AB就可以建立连接相互通信了
四次挥手的原因
因为两台设备已经处于相互联通状态,读、写都是开放的,需要四次操作 关闭各自对 对方 的读、写功能
15,内联函数(inline)与宏函数(#define)的相同点和不同点?
相同点:
两者都是可以加快程序运行效率,使代码变得更加通用
不同点:
1,内联函数的调用是传参,宏定义只是简单的文本替换
2,内联函数可以在程序运行时调用,宏定义是在程序编译进行
3,内联函数有类型检测更加的安全,宏定义没有类型检测
4,内联函数在运行时可调式,宏定义不可以
5,内联函数可以访问类的成员变量,宏不可以
6,类中的成员函数是默认的内联函数
16,指针与引用的相同点和不同点
相同点:
都可以跨函数共享变量、都可以提高函数传参效率、也都可以使用const保护
不同点:
1、引用是一种取别名的机制,指针是一种数据类型
2、引用不需要额外的存储空间,而指针需要4或8字节内存用于存储内存地址编号
3、引用不能更换指向目标,而指针可以更改指向
4、引用必须初始化,而指针可以不初始化
5、有空指针,但没有空引用
6、指针可以配合堆内存使用,引用不可以
7、可以定义指针数组,但不能定义引用数组(可以定义数组指针,也可以定义数组引用,可以定义函数指针,也可以定义函数引用)