面试复习...

来源:互联网 发布:迅雷玩客云抢购软件 编辑:程序博客网 时间:2024/04/29 14:20

const与static的作用:

static:
1)函数体内static变量的作用范围为函数体,和auto变量不同,改变量的内存只被分配一次,因此其值在分配的时候和上次值一样
2)在模块内的static全局变量可以被模块内的所有函数访问,但不能被模块外的其他函数访问
3)在模块内的static函数只可被这一模块内的其他函数调用,这个函数作用访问被限制在其声明模块内
4)在类中的static成员变量属于整个类所有,对类的所有对象只有一份复

5)在类中的static成员函数属于整个类所有,这个函数不接受this指针,所以访问类的static成员变量
const:
1)欲阻止一个变量被改变,可以使用const,在定义const变量时,通常需要对他进行初始化,因为以后就没有机会再改变(例:const int a = 10)
2)对指针来说,可以指定指针的本身为const,也可以指定指针所指向的数为const,或者二者都为const(例:int * const p , const int*p)
3)在一个函数的声明中,const可以修饰形参则表明它是一个输入参数,在函数内,不能改变其值(例:void init(const int p))
4)对于类的成员函数,若指定其为const类型,则表明其是一个常量函数,不能修改类的成员变量
5)对于类的成员函数,有时候必须指定其返回值为const类型,可以使其返回值不为”左值”

用变量a给出下面定义:

1)一个整型(An integer)
int a
2)一个指向整型树的指针(A pointer to an integer)
int *a
3)一个指向指针的指针,它指向的指针指向一个整数型(A pointer to a pointter an intger)
int **a
4)一个有10个整型树的数组(Anarray of 10 integers)
int a[10]
5)一个有10个指针的数组,该指针是指向一个整型数(An array of 10 pointer integet)
int *a[10]
6)一个指向有10个整型数组的指针(A pointer to an array of 10 integer)
int (*a)[10]
一个指向函数的指针,该函数有一个整型参数并返回一个整型树(A pointer to a function that takes an integer as an argument andreturns an integer)
int(*a)(int)
一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to funcions thar take an integer argument return an integer)
int(*a[10])(int)

不使用运算符求和:

int addWithoutArithmetic(int num1,int num2){    if(num2 == 0)        return num1;    int XORresult = num1 ^ num2;    int carry = (num1 & num2)<<1;    return addWithoutArithmetic(XORresult,carry);}

多态,虚函数,纯虚函数
多态:是对于不同对象接收相同信息时产生的不同动作
虚函数:在父类(基类)写上virtual关键字
纯虚函数:在基类为其子类保留一个函数名字,以便子类根据需求对它进行定义,作为接口存在,纯虚函数不具备函数功能

什么是引用,引用和指针的区别?
引用就是给一个变量起一个别名,
1,指针是一个变量,只不过这个变量存储的是一个地址,引用和原来的变量是实质上是同一个东西,只不过给变量起了一个别名
2,可以有const指针,没有const引用
3,指针可以有多级,引用只有一级
4,指针的值可以为NULL,引用的值不能为空
5,指针的值可以初始化后在改变,
6,”sizeof引用”得到是所指向的变量(对象)的大小,而”sizeof()指针”是一个指针的大小

什么时候需要引用
既要利用引用提高程序效率,有要保护传递给函数的数据不在函数中改变,就要常引用

请简述const int p, int const *p, int const p, const int * const p的区别。
const int *p, int const *p:
代表p所指向的内存空间的值是不可以被改变的,但是p本身的值是可以被改变的.
int * const p:
代表p本身值是不可以被改变的,但是p所指向的内存空间中的值是可以被改变的.
const int * const p:
代表p本身的值是不可以被改变的,同时p所指向内存空间中的值也是不可被改变的.
const与#define相比有什么不同
const常量有数据类型,而宏常量没有数据类型
有些集成化的调试工具可以对const常量进行调试,但不能对宏常量进行调试

重载和重写的区别
重载:是指允许存在多个同名变量,而这些函数的参数表不同
重写:指子类重新定义基类的虚函数
* main 函数执行以前,还会执行什么代码?*
全局对象的构造函数
c++是不是安全类型?
不是,两个不同类型的指针之间可以强制转换

描述内存分配方式以及它们的区别?
1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
栈内存与文字常量区
char str1[] = “abc”;
char str2[] = “abc”;
const char str3[] = “abc”;
const char str4[] = “abc”;
const char *str5 = “abc”;
const char *str6 = “abc”;
char *str7 = “abc”;
char *str8 = “abc”;
cout << ( str1 == str2 ) << endl;//0 分别指向各自的栈内存
cout << ( str3 == str4 ) << endl;//0 分别指向各自的栈内存
cout << ( str5 == str6 ) << endl;//1指向文字常量区地址相同
cout << ( str7 == str8 ) << endl;//1指向文字常量区地址相同
结果是:0 0 1 1
解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。

子类析构时要调用父类的析构函数吗?
析构函数调用次序是先子类的析构在父类析构,也就是说:父类在调用时,子类的信息全部销毁了,定义一个对象时先调用基类的构造函数,然后调用子类的构造函数,析构函数刚刚相反..

基类的析构函数不是虚函数,会带来什么问题?
派生类的析构函数用不上,会造成资源的泄漏。

函数指针与指针函数
pass
单例和观察者模式
单例模式:一个类中有且只有一个对象(只能实例化一次,不能拷贝,不能赋值)
观察者模式: (最近中国股市起起伏伏,当然了起伏就用商机,小明发现商机后果断想入市,买入了中国证券,他想在电脑客户端上,网页上,手机上,iPad上都可以查看到该证券的实时行情,们可以这样:小明的所有客户端上都订阅中国证券这个股票,只要股票一有变化,所有的客户端都会被通知到并且被自动更新。)
观察者模式就是定义对象间的一种一对多的依赖关系,当一个对象发生改变时,所有依赖它的对象得到通知,并自动更新
public,private,protected
(类外)可以访问public成员而不能访问private成员;private成员只能由类成员(类内)和友元访问; protected 成员可以被子类对象访问,不能被用户代码(类外)访问

socket通信步骤
这个东西面试忘记了,然后就挂了
tcp/ip客户端步骤:

                                        服务器端:1,创建一个socket                            创建一个socket2,初始化ip地址和端口号                       初始化ip地址和端口号3,连接(connect)                           绑定(bind)4,send                                  listen5,recv                                  accept6,关闭socket                                  -----等待7,                                      recv8,                                      send9                                       关闭socket                                    UDP服务器端:                            客户端                                     1,创建socket                              创建socket2,初始化ip地址和端口号                       初始化ip地址和端口号3,bind                                  bind4recvfrom--等待---                             4,sendto5,sendto                                recvfrom6,关闭                                    关闭

今天面试一家游戏公司,一上来就问我后台服务器让你来搭建你要怎么搭建,一开始一脸懵逼,后面技术官一步一步的详细的问,然后我感觉游戏后台的服务器搭建 先确认前端发送来的数据格式在建立socket通信 利用libevent来构建服务器,如达到百万级的用户访问,可以利用libevent来搭建高并发,然后和我说了整个服务器搭建过程中最主要的是什么,我一开始说的是数据…(因为数据丢失的话..用户的资料就没了),然后他再问还有什么…我仔细想想了一下 说数据库…. 确实数据储存的数据表 没有了 就什么都没有了….还有缓冲机制 这个很重要..以前没有部署过整个服务器的搭建..都是组长分配下来的模块 哎…确实有点懵逼..但是面试官一步一步详细的进入说, 我还是能说出一点服务器毛皮来……虽然复试,但我感觉没希望,呵呵!

–与零值比较
BOOL类型:

if(flag)if(!flag)

整型类型

if(i==0)if(i!=0)

浮点类型(float,double)

if((x>=-Q)&&(x<=Q))

指针类型

if(p==NULL)if(p!=NULL)

thread:

线程的基本概念、线程的基本状态及状态之间的关系

int pthread_create(pthread_t * thread,const pthread_adttr *attr,void*(*start_routine),void*arg

第一个参数:指向线程标识符pthread_t的指针;
第二个参数:设置线程的属性
第三个参数:线程运行函数的起始地址
第四个参数:运行函数的参数
基本概念:线程,即轻量级进程(LWP:Light Weight Process),是程序执行流的最小单元。一个标准的线程由线程ID、当前指令指针(PC),寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程不拥有系统资源,近拥有少量运行必须的资源。

基本状态:就绪、阻塞和运行三种基本状态。

就绪状态,指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;

运行状态,指线程占有处理机正在运行;

阻塞状态,指线程在等待一个事件(如信号量),逻辑上不可执行。
执行

时间片完成 – 调度 — I/O请求
—- —-
就绪 阻塞
I/O完成

int select(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfs,struct timeval *time)第一个参数为文件的句柄(ID)+1,第二个参数文件的编号,第三第四同上,第五定义一个超时结构体
下面几个函数(宏实现)用来操纵文件描述符集:void FD_SET(int fd, fd_set *set);   //在set中设置文件描述符fdvoid FD_CLR(int fd, fd_set *set);   //清除set中的fd位int  FD_ISSET(int fd, fd_set *set); //判断set中是否设置了文件描述符fdvoid FD_ZERO(fd_set *set);          //清空set中的所有位(在使用文件描述符集前,应该先清空一下)

int select(int nfds,fd_set *readfds,fd_set*writefds,fd_set*exceptfs,struct timeval *time)

int epoll_create(int size);
//调用成功返回一个文件描述符,失败返回-1并设置errno。
线程和进程的区别

```int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);    //调用成功返回0,调用失败返回-1并设置errno。
epfd是epoll_create返回的文件句柄,标识事件表,op指定操作类型。操作类型有以下3种:a)EPOLL_CTL_ADD, 往事件表中注册fd上的事件;b)EPOLL_CTL_MOD, 修改fd上注册的事件;c)EPOLL_CTL_DEL, 删除fd上注册的事件。

select和epoll的区别

相同点:都需要在fd上注册用户关心的事件都要一个timeout参数指定超时时间不同点:select指定了三个文件描述符,即可读,可写,异常事件,所以不能更加的细致的区分所以可能发生的事情如果检测到就绪事件,会在原来的文件描述符中改动,所以再次调用select时候必须重置文件描述符select使用对所有注册的文件描述符集轮询的方式,会返回整个用户注册的事件集合,最大的监听描述符有限制,一般是1024,只能工作LT模式(LT是缺省的工作方式)(ET是高速工作方式)epoll:它把用户注册的文件描述符和事件放到内核中的事件表中提供一个独立的函数来管理 epoll_ctLl 来管理用户事件,epoll采用回调方式.一旦有注册文件描述符就绪,触发回调函数,该回调函数把就绪文件描述符和事件拷贝到用户空间event内存中,epoll_wait使用maxevents来制定最多监听多少个文件描述符和事件,这个数能达到系统允许打开的最大文件描述符数目,即65535

LT(水平触发)和ET(边沿触发)
区别:LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读取,则不断的通知你。而ET则只在事件发生之时通知。

进程和线程的区别

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个线程死掉

多线程同步和互斥有几种实现方法

用户模式下:原子操作,临界区
内核模式下:事件,互斥,信号量

在Windows编程中互斥量与临界区比较类似,请分析一下二者的主要区别。

临界区:不是内核对象,速度快,适合控制数据访问
互斥量:内核对象,速度,慢对一个共享资源的单独访问而设计

进程间通信方式

管道,有名管道,信号,消息队列,共享内存,信号量,套接字

什么是页抖动?

在短时间内出现大量页错动作的情形被称为页抖动,它将大大降低系统的执行效率

I/O模式 阻塞与非阻塞

非阻塞IO 和阻塞IO:

   在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:   基本概念:          阻塞IO::                 socket 的阻塞模式意味着必须要做完IO 操作(包括错误)才会                 返回。          非阻塞IO::                 非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方                 式来判断具体操作是否成功。

IO模式设置:

                    SOCKET   对于一个socket 是阻塞模式还是非阻塞模式有两种方式来处理::   方法1、fcntl 设置;用F_GETFL获取flags,用F_SETFL设置flags|O_NONBLOCK;   方法2、recv,send 系列的参数。(读取,发送时,临时将sockfd或filefd设置为非阻塞)                          方法一的实现

fcntl 函数可以将一个socket 句柄设置成非阻塞模式:
flags = fcntl(sockfd, F_GETFL, 0); //获取文件的flags值。

  fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);   //设置成非阻塞模式;  flags  = fcntl(sockfd,F_GETFL,0);  fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK);    //设置成阻塞模式;  设置之后每次的对于sockfd 的操作都是非阻塞的。                                       方法二的实现recv, send 函数的最后有一个flag 参数可以设置成MSG_DONTWAIT         临时将sockfd 设置为非阻塞模式,而无论原有是阻塞还是非阻塞。recv(sockfd, buff, buff_size,MSG_DONTWAIT);     //非阻塞模式的消息发送send(scokfd, buff, buff_size, MSG_DONTWAIT);   //非阻塞模式的消息接受          普通文件    对于文件的阻塞模式还是非阻塞模式::    方法1、open时,使用O_NONBLOCK;    方法2、fcntl设置,使用F_SETFL,flags|O_NONBLOCK;              消息队列    对于消息队列消息的发送与接受::    //非阻塞  msgsnd(sockfd,msgbuf,msgsize(不包含类型大小),IPC_NOWAIT)    //阻塞     msgrcv(scokfd,msgbuf,msgsize(**),msgtype,IPC_NOWAIT);       读                

阻塞与非阻塞读的区别: //阻塞和非阻塞的区别在于没有数据到达的时候是否立刻返回.

读(read/recv/msgrcv):

   读的本质来说其实不能是读,在实际中, 具体的接收数据不是由这些调用来进行,是由于系统底层自动完成的。read 也好,recv 也好只负责把数据从底层缓冲copy 到我们指定的位置.   对于读来说(read, 或者recv) ::

阻塞情况下::

   在阻塞条件下,read/recv/msgrcv的行为::   1、如果没有发现数据在网络缓冲中会一直等待,   2、当发现有数据的时候会把数据读到用户指定的缓冲区,但是如果这个时候读到的数据量比较少,比参数中指定的长度要小,read 并不会一直等待下去,而是立刻返回。   read 的原则::是数据在不超过指定的长度的时候有多少读多少,没有数据就会一直等待。   所以一般情况下::我们读取数据都需要采用循环读的方式读取数据,因为一次read 完毕不能保证读到我们需要长度的数据,   read 完一次需要判断读到的数据长度再决定是否还需要再次读取。

非阻塞情况下::

   在非阻塞的情况下,read 的行为::   1、如果发现没有数据就直接返回,   2、如果发现有数据那么也是采用有多少读多少的进行处理.         所以::read 完一次需要判断读到的数据长度再决定是否还需要再次读取。

对于读而言:: 阻塞和非阻塞的区别在于没有数据到达的时候是否立刻返回.
recv 中有一个MSG_WAITALL 的参数::

   recv(sockfd, buff, buff_size, MSG_WAITALL),   在正常情况下recv 是会等待直到读取到buff_size 长度的数据,但是这里的WAITALL 也只是尽量读全,在有中断的情况下recv 还是可能会被打断,造成没有读完指定的buff_size的长度。   所以即使是采用recv + WAITALL 参数还是要考虑是否需要循环读取的问题,在实验中对于多数情况下recv (使用了MSG_WAITALL)还是可以读完buff_size,   所以相应的性能会比直接read 进行循环读要好一些。

注意:: //使用MSG_WAITALL时,sockfd必须处于阻塞模式下,否则不起作用。

           //所以MSG_WAITALL不能和MSG_NONBLOCK同时使用。   要注意的是使用MSG_WAITALL的时候,sockfd 必须是处于阻塞模式下,否则WAITALL不能起作用。                                                                                                                                           写 

阻塞与非阻塞写的区别: //
写(send/write/msgsnd)::

   写的本质也不是进行发送操作,而是把用户态的数据copy 到系统底层去,然后再由系统进行发送操作,send,write返回成功,只表示数据已经copy 到底层缓冲,而不表示数据已经发出,更不能表示对方端口已经接收到数据.   对于write(或者send)而言,

阻塞情况下:: //阻塞情况下,write会将数据发送完。(不过可能被中断)

   在阻塞的情况下,是会一直等待,直到write 完,全部的数据再返回.这点行为上与读操作有所不同。    原因::          读,究其原因主要是读数据的时候我们并不知道对端到底有没有数据,数据是在什么时候结束发送的,如果一直等待就可能会造成死循环,所以并没有去进行这方面的处理;          写,而对于write, 由于需要写的长度是已知的,所以可以一直再写,直到写完.不过问题是write 是可能被打断吗,造成write 一次只write 一部分数据, 所以write 的过程还是需要考虑循环write, 只不过多数情况下一次write 调用就可能成功.

非阻塞写的情况下:: //

   非阻塞写的情况下,是采用可以写多少就写多少的策略.与读不一样的地方在于,有多少读多少是由网络发送的那一端是否有数据传输到为标准,但是对于可以写多少是由本地的网络堵塞情况为标准的,在网络阻塞严重的时候,网络层没有足够的内存来进行写操作,这时候就会出现写不成功的情况,阻塞情况下会尽可能(有可能被中断)等待到数据全部发送完毕, 对于非阻塞的情况就是一次写多少算多少,没有中断的情况下也还是会出现write 到一部分的情况.

socket阻塞好像直接调用accept 阻塞
socket设置非阻塞:
方法one —fcntl 文件控制函数

int flag;if (flag = fcntl(fd, F_GETFL, 0) <0)     perror("get flag");flag |= O_NONBLOCK;if (fcntl(fd, F_SETFL, flag) < 0)    perror("set flag");

方法two–ioctl //设备控制接口函数

int b_on = 1;ioctl (fd, FIONBIO, &b_on);

绝对地址0xFFFFFFFFFF写入字符’a’:

unsigned char p = (unsigned char )0xFFFFFFFFFF; *p=’a’;

队列和栈的区别:

队列先进先出,栈先进后出;
遍历数据速度不同 栈只能从头部取数据,要先遍历整个栈才能把最先放进去的取出来要开辟临时空间,队列基于地址指针遍历,可以从头或尾进行遍历,无需开辟临时空间

static全局变量与普通全局变量的区别?static局部变量与普通局部变量的区别?static全局函数与普通全局函数的区别?

static 全局变量只初始化一次,防止在其他文件单元中引用(全局变量的作用域限于一个源文件内,只能为该源文件内的函数公用)
普通全局变量在各个源文件都有效
static函数与普通函数作用域不同,仅在本文件,内部函数应该在当前源文件说明和定义,普通函数包含头文件就能使用
static变量,开始定义时赋值,以后进入子函数时之前在此函数中计算的值重新赋值该变量
局部变量:每次进入进入子函数中重新赋值
在c++类中 static属于整个类所有,对类的所有对象只有一份拷贝
类中的static成员函数属于整个类所有,不接受this指针,只能访问static成员变量

Linux中五种状态:
1运行,中断,不可中断,僵死,停止
Linux中僵尸进程与孤儿进程
僵尸进程:是因为子进程运行结束(一般是调用exit、运行时发生致命错误或收到终止信号所导致)父进程没有给儿子收尸,所以变成僵尸进程(结束僵尸进程可以把僵尸爸爸干掉,然后子进程就过继给1号进程init 然后系统自动清理)
孤儿进程:一个父进程退出,而它的一个或多个子进程还在进行;僵尸进程会造成资源浪费,孤儿进程不会
Linux 修改单个进程最大允许的文件句柄(首先可以使用ulimit -n 查看单个进程的打开的最大文件句柄 socket 也算在里面 系统默认值是1024)
方法一:
修改/etc/security/limit.conf
里面有很详细的注释,比如
* soft nofile 32768
* hard nofile 65536
就可以将文件句柄限制统一改成软32768,硬65536.配置文件最前面的是指domain,设置为星号代表全局,另外你也可以针对不同的用户做出不同的限制
方法二:
vi /etc/profile 增加ulimit -HSn 65536
vi /etc/security/limit.conf 为你的用户增加
soft nofile 32768
hard nofile 65536
ulimit命令查看设置是否成功
查看系统总限制的命令:
#cat /proc/sys/fs/file-max
查看整个系统目前使用的文件句柄数量命令:
#cat /proc/sys/fs/file-nr
查找文件句柄问题的时候,还有一个很实用的程序lsof,可以很方便看到某个进程开了哪些句柄:
#lsof -p pid
某个进程开了几个句柄:
#lsof -p pid |wc –l
也可以看到某个目录 /文件被什么进程占用了,显示已打开该目录或文件的所有进程信息:
#lsof path/filename
查看磁盘大小
可以使用df和du (df-k) du -k /home ….
运行的进程和项目的各种统计信息
prstat -J
查看cpu使用率:top
查看内存大小
free -m

STL 嵌套使用:

#include<iostream>#include<list>#include<string>#include<map>using namespace std;void main(){    list < list<map<int, string>>>l;    map<int, string>m1;    m1.insert(pair<int, string>(1, "1"));    m1.insert(pair<int, string>(2, "2"));    list<map<int, string>>ol;    ol.push_back(m1);    l.push_back(ol);    for (auto it = l.begin(); it != l.end(); it++)    {        for (auto it1 = (*it).begin(); it1 != (*it).end(); it1++)        {            for (auto it2 = (*it1).begin(); it2 != (*it1).end(); it2++)            {                cout << "value:" << it2->first << " " << it2->second << endl;            }        }    }}void main01(){    list<list<int>>l;    list<int>ol;    ol.push_back(1);    ol.push_back(2);    l.push_back(ol);    for (auto it = l.begin(); it != l.end(); it++)    {        for (auto it1 = (*it).begin(); it1 != (*it).end(); it1++)        {            cout << "value : " << *it1 << endl;        }    }}

define 的替换

记住#define 只是替换,只是替换,只是替换

#define a(x)x+xvoid main01(){int m =1,n = 2,k = 3;int sum =a(m+n)*k//只是替换 m+n+m+n*k -->1+2+1+2*3 -->1+2+1+6=10}#define m(x)x*(x-1)void main02(){    int a = 1,b =2;    printf("%d\n",m(1+a+b));    //替换 1+1+2*(1+1+2-1)=8}#define P 5.5#define S(X)P*X*Xvoid main(){int a = 1,b=2;printf("%.1lf\n",S(a+b));//5.5 * 1+2*1+2=9.5#define MIN (X,Y)(X)<(Y)?(X):(Y)void main(){int i =10,j = 15;int sum = 10*MIN(i,j);//最恶心的来了: 替换为10 * (X)<(Y)?(X):(Y) -->10*(10)<(15)?(10):(15) -->100 <15 ? 10:15 结果是15}

c语言中的malloc和c++中的new的区别
new是c++的操作符,malloc是c的一个函数
new不止分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数

原创粉丝点击