1.const与define 相比,有何优点
1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
2.头文件中的ifndef define endif 的作用
3.在c++程序中调用被C编译器编译后的函数,为什么要加extern“C”
C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在库中的名字与C语言的不同,
假设某个函数原型为:
void foo(int x, inty);
该函数被C编译器编译后在库中的名字为: _foo
而C++编译器则会产生像: _foo_int_int 之类的名字。
为了解决此类名字匹配的问题,C++提供了C链接交换指定符号 extern "C"。
4.描述new、delete、malloc、free的关系
5.拷贝构造函数什么时候被调用?什么情况下必须重写?
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化以上的情况就需要拷贝构造函数的调用。
当类中的数据成员需要动态分配存储空间时,不可以依赖default copy constructor。当default copy constructor被因编译器需要而合成时,将执行default memberwise copy语义。此时如果类中有动态分配的存储空间时,将会发生惨重的灾情。在需要时(包括这种对象要赋值、这种对象作为函数参数要传递、函数返回值为这种对象等情况),要考虑到自定义拷贝构造函数。
6基类的析构函数不是虚函数,会带来什么问题
7.将“引用”作为函数返回值类型的格式,好处和需要遵守的规则
格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }
好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!
注意事项:
(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)流操作符重载返回值申明为“引用”的作用:
流操作符 < <和>>,这两个操作符常常希望被连续使用,例如:cout < < "hello" < < endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个 < <操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用 < <操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。
赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。
#include <iostream.h>
int &put(int n);
int vals[10];
int error=-1;
void main()
{
put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;
put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20;
cout < <vals[0]; cout="cout" int="int" put="put" n="n" if="if" n="n">=0 && n <=9 ) return vals[n];
else { cout < <"subscript error"; return error; }
}
(5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。
8.描述内存分配方式以及它们的区别
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
9.重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义父类虚函数的方法。
从实现原理上来说:
重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。
10.描述TCP建立连接和断开连接的过程
三次握手:
第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
四次握手
与建立连接的“三次握手”类似,断开一个TCP连接则需要“四次握手”。
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不
会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可
以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
11.在浏览器输入网址后回车执行的具体步骤
一、URL自动解析 HTTP
URL自动解析 HTTP,URL包含了用于查找某个资源的足够信息,基本格式如下:HTTP://host[“:”port][abs_path],其中HTTP表示桶盖HTTP协议来定位网络资源;host表示合法的主机域名或IP地址,port指定一个端口号,缺省80;abs_path指定请求资源的URI;如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动帮我们完成。例如:输入www.qcloud.com;浏览器会自动转换成:http://www.qcloud.com/
二、浏览器查找域名的IP地址
获取IP,建立TCP连接 浏览器地址栏中输入 www.xxx.html 并提交之后,首先它会在浏览器缓存、系统缓存、路由器缓存中查找,如果有则直接告诉IP地址。如果没有则在网关DNS服务器进行查找,找到对应的IP后,则返回会给浏览器。
三、浏览器给web服务器发送一个HTTP请求
当获取IP之后,就开始与所请求的Tcp建立三次握手连接,连接建立后,就向服务器发出HTTP请求。
浏览器会把以下请求发送到腾讯云所在的服务器:
GET http://www.qcloud.com/ HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: facebook.com
Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]
复制代码
GET这个请求定义了要读取的URL,浏览器自定义(User-Agent头)和它希望接受什么类型的相应(Accept and Accept-Encoding头)Connection头要求服务器为了后边的请求不要关闭TCP连接。
请求中也包含浏览器存储的该域名的cookies,在不同页面请求当中,cookie是与跟踪一个网站状态相匹配的键值。这样cookies会存储登录用户名,服务器分配的密码和一些用户设置等。Cookies会以文本文档形式存储在客户机里,每次请求时发送给服务器。
四、Web服务器应答,并向浏览器发送数据
客户机向服务器发出请求后,服务器会客户机回送应答,HTTP/1.1 200 OK应答的第一部分是协议的版本号和应答状态码,正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据
五、Web服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码Connection:keep-aliveTCP连接在发送后将仍然保持打开状态。
六、浏览器解析网页内容。
由于网页可能有重定向,或者嵌入了图片,AJAX,其它子网页等等,这4个步骤可能反复进行多次才能将最终页面展示给用户。
12.ARP是地址解析协议,简单解释一下工作原理
2:当源主机要发送数据时,首先检查ARP列表中是否有对应IP地址的目的主机的MAC地址,如果有,则直接发送数据,如果没有,就向本网段的所有主机发送ARP数据包,该数据包包括的内容有:源主机IP地址,源主机MAC地址,目的主机的IP地址。
3:当本网络的所有主机收到该ARP数据包时,首先检查数据包中的IP地址是否是自己的IP地址,如果不是,则忽略该数据包,如果是,则首先从数据包中取出源主机的IP和MAC地址写入到ARP列表中,如果已经存在,则覆盖,然后将自己的MAC地址写入ARP响应包中,告诉源主机自己是它想要找的MAC地址。
4:源主机收到ARP响应包后。将目的主机的IP和MAC地址写入ARP列表,并利用此信息发送数据。如果源主机一直没有收到ARP响应数据包,表示ARP查询失败。
广播发送ARP请求,单播发送ARP响应。
13.描述TCP拥塞控制的实现机制
慢启动算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window), 记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。发送方开始时发送一个报文段,然后等待ACK。当收到该ACK时,拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的ACK时,拥塞窗口就增加为4,这是一种指数增加的关系。在某些点上可能达到了互联网的容量,于是中间路由器开始丢弃分组。拥塞避免算法是一种处理丢失分组的方法。该算法假定由于分组受到损坏引起的丢失是非常少的(远小于1%),因此分组丢失就意味着在源主机和目的主机之间的某处网络上发生了拥塞。有两种分组丢失的指示:发生超时和接收到重复的确认。拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希望降低分组进入网络的传输速率,于是可以调用慢启动来作到这一点。在实际中这两个算法通常在一起实现。1990年出现的TCP Reno版本增加了"快速重传"算法、"快速恢复"算法,避免了当网络拥塞不够严重时采用"慢启动"算法而造成过大地减小发送窗口尺寸的现象。
拥塞控制的四个阶段
a.慢启动阶段(slow start):发送方一开始便向网络发送多个报文段,直至达到接收方通告的窗口大小为止。当发送方和接收方处于同一个局域网时,这种方式是可以的。但是如果在发送方和接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题。一些中间路由器必须缓存分组,并有可能耗尽存储器的空间。
b.拥塞避免阶段(congestion avoidance):当发现超时或收到3个相同ACK确认帧时,则表示有丢包事件,此时网络已发生拥塞现象,此时要进行相应的拥塞控制。将慢启动阈值设置为当前拥塞窗口的一半;如检测到超时,拥塞窗口就被置为l。如果拥塞窗口小于或等于慢启动阈值,TCP重新进人慢启动阶段;如果拥塞窗口大于慢启动阈值,TCP执行拥塞避免算法。
c.快速重传阶段(fast retransmit):当TCP源端收到到三个相同的ACK副本时,即认为有数据包丢失,则源端重传丢失的数据包,而不必等待RTO超时。同时将ssthresh设置为当前cwnd值的一半,并且将cwnd减为原先的一半。
d.快速恢复阶段(fast recovery) :当"旧"数据包离开网络后,才能发送"新"数据包进入网络,即同一时刻在网络中传输的数据包数量是恒定的。如果发送方收到一个重复的ACK,则认为已经有一个数据包离开了网络,于是将拥塞窗口加1。
14.一个后台server的socket调用流程
bind
listen
select epoll
accept
recv
send
15.EPOLL的优点以及两种工作模式的区别
突破了单进程打开SOCKET描述符最大数目的限制,select单进程打开FD的数据是有限制的,由FD_SETSIZE设置,默认值是2048,而这在那此需要支持上万连接数目的网络服务器来说是不能忍受的,虽然这个限制可以通过修改宏重编译内核,但这会给效率带来急剧下降,而epoll却没有这个限制,理论上打开FD的数目跟系统内在有关;
另外效率不会随连接数的增加而线性下降,它只会对活跃的IO事件进行操作,而select每次调用返回的是线性扫描的全集,不过在一个高速的网络环境里epoll的性能并不比select高多少,因为大部分的socket基本都是活跃的
Edge Triggered (ET)、Level Triggered (LT);
ET(edge-triggered)是高速工作方式,只支持no-block socket。当IO事件发生时内核通知你后不会再发送更多的通知,一直到你执行的操作导致那个文件描述符事件的改变,如果你不对其进程IO操作,内核不会再通知你。
Level Triggered (LT)是缺省的工作方式,并且同时支持block和no-block socket.内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,你可以使用EPOLLONESHOT标志来告诉内核只通知一次,需注意的是操作完后使用带有 EPOLL_CTL_MOD标志epoll_ctl修改处理文件描述符
16.TIME_WAIT出现的原因和一般的处理方法
原因:
TIME_WAIT的状态就是主动断开的一方,发送完最后一次ACK之后进入的状态。
优化方法:
1)可以改为长连接,但代价较大,长连接太多会导致服务器性能问题,而且PHP等脚本语言,需要通过proxy之类的软件才能实现长连接;
2)修改ipv4.ip_local_port_range,增大可用端口范围,但只能缓解问题,不能根本解决问题;
3)程序中设置socket的SO_LINGER选项;
4)打开tcp_tw_recycle和tcp_timestamps选项;
5)打开tcp_tw_reuse和tcp_timestamps选项;
6)设置tcp_max_tw_buckets为一个很小的值
17.为脚本程序指定执行权的命令
chmod 777
18.查看磁盘利用率以及目录下文件大小的命令
df19.进程间通讯的方式有哪些,各有什么优缺点
进程间通信主要包括管道, 系统IPC(包括消息队列,信号量,共享存储), socket.
管道包括三种:
1)普通管道PIPE, 通常有种限制,一是半双工,只能单向传输;二是只能在父子进程间使用.
2)流管道s_pipe: 去除了第一种限制,可以双向传输.
3)命名管道:name_pipe, 去除了第二种限制,可以在许多并不相关的进程之间进行通讯.
系统IPC的三种方式类同,都是使用了内核里的标识符来识别
管道: 优点是所有的UNIX实现都支持, 并且在最后一个访问管道的进程终止后,管道就被完全删除;缺陷是管道只允许单向传输或者用于父子进程之间
系统IPC: 优点是功能强大,能在毫不相关进程之间进行通讯; 缺陷是关键字KEY_T使用了内核标识,占用了内核资源,而且只能被显式删除,而且不能使用SOCKET的一些机制,例如select,epoll等.
socket可以跨网络通讯,其他进程间通讯的方式都不可以,只能是本机进程通讯。
20.linux多线程同步的方法
2:互斥锁,就是在各个线程要使用的一些公共数据之前加锁,使用之后释放锁,这个是非常常用的线程安全控制的方法,而频繁的加解锁也对效率有一定的影响。
3:条件变量,而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
4:信号量,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本
21.描述一下快速排序
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
22.描述一下数据库设计的三大范式
1.第一范式1NF(域的原子性)第一范式是对域的原子性的一个要求,在数据库设计中,一般都应该满足第一范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式,不过有些关系模型中突破了第一范式的限制,这种称为非第一范式的关系模型。换句话说,是否必须满足第一范式的最低需求,主要依赖所使用的关系模型。
2.第二范式2NF(表中除主键外的字段都完全依赖主键)
第二范式是在第一范式基础上建立的。第二范式有两个重点:(1)表中必须有主键;(2)其他非主属性必须完全依赖主键,不能只依赖主键的一部分(主要针对联合主键而言)。
3.第三范式3NF(表中除主键外的字段都完全直接依赖,不能是传递依赖)
不能是传递依赖,即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。
23.什么是SQL注入,如何防护
防护
归纳一下,主要有以下几点:
1.永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和
双"-"进行转换等。
2.永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4.不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
5.应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
24.什么是XSS漏洞,如何防护
什么是XSS
XSS又叫CSS (Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意的特殊目的。XSS属于被动式的攻击,因为其被动且不好利用,所以许多人常呼略其危害性。
XSS攻击解决办法
请记住两条原则:过滤输入和转义输出。
具体执行的方式有以下几点:
第一、在输入方面对所有用户提交内容进行可靠的输入验证,提交内容包括URL、查询关键字、http头、post数据等
第二、在输出方面,在用户输内容中使用 <xmp>标签。标签内的内容不会解释,直接显示。
第三、严格执行字符输入字数控制。
四、在脚本执行区中,应绝无用户输入。