深信服历年笔试
1.. release版本的可执行程序为什么非常大?
程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用
Release和Debug有什么不同
Release版称为发行版,Debug版称为调试版。
Debug中可以单步执行、跟踪等功能,但生成的可执行文件比较大,代码运行速度较慢。Release版运行速度较快,可执行文件较小,但在其编译条件下无法执行调试功能。
Release的exe文件链接的是标准的MFC DLL(Use MFC in a shared or static dll)。这些DLL在安装Windows的时候,已经配置,所以这些程序能够在没有安装Visual C++ 6.0的机器上运行。而Debug版本的exe链接了调试版本的MFC DLL 文件,在没有安装Visual C++6.0的机器上不能运行,因为缺相应的DLL,除非选择use static dll when link。
sizeof(d)? 为什么在不同的平台上得到的值不一样?
C++拷贝构造函数和赋值运算符有那些不同和相同点。
拷贝构造函数和赋值号的异同
同:都可以对对象的成员进行赋值
异:
拷贝构造函数首先是一个构造函数,它调用的时候产生一个对象,是通过参数传进来的那个对象来初始化,产生的对象。
赋值是把一个对象赋值给一个原有的对象,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。
软件开发过程包含哪些阶段?各阶段质量保证措施是什么?
、1.需求分析
需求分析是开发人员对系统需要做什么和如何做的定义过程。从系统分析的经验来看,这个过程往往是个循序渐进的过程,一次性对系统形成完整的认识是困难的。只有不断地和客户领域专家进行交流确认,方能逐步明了用户的需求。从系统开发的过程得知,系统分析时犯下的错误,会在接下来的阶段被成倍的放大,越是在开发的后期,纠正分析时犯下的错误所花费的代价越是昂贵,也越发影响系统的工期和系统的质量。
解决系统分析错误的方法我们公司通常采用邀请用户参与进行需求评定,然后对其用户的意见由质保成员跟踪检测是否纳入需求规格说明书,同时与用户签字确认形成需求基线,交由配置管理员放入配置管理库。
b、系统设计
优良的体系结构应当具备可扩展性和可配置性,而好的体系结构则需要好的设计方法,自然设计选型成为了系统设计首要的工作,究竟是采用哪种设计方法好呢?
对于设计选型不能一概而论,需要针对项目的结构、项目的特征和用户的需求来分析,同样也要考虑到参与项目小组成员的素质,如果其中大部分都没有从事过面向对象的设计且项目进对紧迫,这样没有多余的时间来培训小组成员来掌握面向对象的设计方法,尽管众所周知面向对象设计方法的优势,我们还是不如采用面向过程的方式(除用户指定开发设计方式外)可以减少项目承担的技术风险。
c、实现
实现也就是代码的生产过程。这里不仅包括代码的产生,同时也包括测试用例的产生。针对上一阶段提供详细设计,程序员开始编码并且调试程序,测试人员则根据设计进行测试用例的设计,设计出来的用例需要得到项目组成员认可由项目经理审核通过才能进入配置库。同时程序员调试完程序提交测试人员进行程序正确性检测。
d、文档管理
文档维护主要是配置管理小组的工作。文档从用途上分主要分为内部文档和外部文档。
内部文档包括:项目开发计划;需求分析;体系结构设计说明;详细设计说明;构件索引;构件成分说明;构件接口及调用说明;组件索引;组件接口及调用说明;类索引;类属性及方法说明;测试报告;测试统计报告;质量监督报告;源代码;文档分类版本索引;软件安装打包文件。
外部文档主要包括:软件安装手册;软件操作手册;在线帮助;系统性能指标报告;系统操作索引。
3、系统维护质量保证
2.使用C++赋值运算符应注意什么地方?
如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能
用“引用传递”,否则会出错。
对于赋值函数,应当用“引用传递”的方式返回String对象。如果用“值传递”的方式,虽然功能仍然正确,但由于return
语句要把*this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。
对于相加函数,应当用“值传递”的方式返回String对象。如果改用“引用传递”,那么函数返回值是一个指向局部对象temp
的“引用”。由于temp在函数结束时被自动销毁,将导致返回的“引用”无效
3.exit()和_exit() 的区别。
exit()?与…_exit()?的基本区别在于前一个调用实施与调用库里用户状态结构(user-mode constructs)有关的清除工作(clean-up),而且调用用户自定义的清除程序,在退出程序前,关闭文件,清除缓存。后一个函数只为进程实施内核清
除工作。不关闭文件,不清楚缓存。
4.哪些方法可让一个进程仅有一个实例运行?
设置临界区,(可以定义自旋锁不?)使用Microsoft提供的互斥类Mutex
使用API函数,获取当前进程,遍历正在有相同名字运行的进程。
红黑树比A VL树的优势在哪?
红黑树引入了“颜色”的概念。引入“颜色”的目的在于使得红黑树的平衡条件得以简化。正如著名的密码学专家Bruce Schneier所说的那样
,“Being Partly balanced can be good enough”,红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了
对旋转的要求
,从而提高了性能。红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不
平衡都会在三次旋转之内
解决。当然,还有一些更好的,但实现起来更复杂的数据结构能够做到一步旋转之内达到平衡,但红黑树能够给我们
一个比较“便宜”的解决
方案。红黑树的算法时间复杂度和A VL相同,但统计性能比A VL树更高。
5.阻塞模式的recv在没受到数据的情况下如何返回?(不能将socket修改为非阻塞)
recv(fd,buf,sizeof(buf),flag)函数原型
将recv()函数的标志位设为停止等待或者设置一个延时退出,超时返回什么的
recv(fd, buf, sizeof(buf), MSG_DONTW AIT);
这里采用了MSG_DONTWAIT标志,它的作用是告诉recv()函数如果有数据到来的话就接受全部数据并立刻返回,
没有数据的话也是立刻返回,而不进行任何的等待。这里的MSG_DONTW AIT 是我们自己定义的一个标志。
6.strcpy()为什么会造成缓冲区溢出?可用哪个函数替代?
造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数,strcpy()函数将源字符串复制到缓冲区。没有指定要复
制字符的具体数目。复制字符的数目直接取决于源字符串中的数目。如果源字符串碰巧来自用户输入,且没有专门限
制其大小,则有可能会导致缓冲区溢出。可用strncpy() 函数替代
7.哪些方法可以避免或减少锁的使用
8. 给定一个int型数n,写一个尽可能简单的函数判断n是否为2的幂,不能用循环。
答:bool foo(int n)
{
int a=n;
if(n<=0) return false;
a=((a&0xAAAAAAAA)>>1)+(a&0x55555555); a=((a&0xCCCCCCCC)>>2)+(a&0x33333333); a=((a&0xF0F0F0F0)>>4)+(a&0x0F0F0F0F); a=((a&0xFF00FF00)>>8)+(a&0x00FF00FF); a=((a&0xFFFF0000)>>16)+(a&0x0000FFFF);
if(a==1) return true;
else return false;
}
9.能识别正则表达式的命令有:grep egrep
10..VC中有哪些方法避免C编译头文件重复。
#ifndef #define #endif #pragma once
11.extern "C"的用法。
用于提供C 接口,如使用 C 命名方式等
作为extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。
extern "C"是连接申明(linkage declaration),被extern "C"修饰的变量和函数是按照C语言方式编译和连接的
.
12.异步socket编程中,send不出数据的错误码是什么,(举Linux或Windows为例),你是怎么处理的?
非阻塞SOCKET,SEND不出数据的原因,TCP下连接断开了和该SOCKET处在阻塞状态(也就是说在发送数据中)。UPD发不出可能是SOCKET处于阻塞状态。
处理的办法就是记录下该SOCKET的状态,当状态为阻塞的时间,放入缓冲,当该SOCKET再次可写时,发送。
13..函数前的static和volatile变量中关键字的作用
(1)auto
这个这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。这个关键字不怎么多写,因为所有的变量默认就是auto的。
(2)register
这个关键字命令编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。
(3)static
常见的两种用途:
1>统计函数被调用的次数;
2>减少局部数组建立和赋值的开销.变量的建立和赋值是需要一定的处理器开销的,特别是数组等含有较多元素的存储类型。在一些含有较多的变量并且被经常调用的函数中,可以将一些数组声明为static类型,以减少建立或者初始化这些变量的开销.
详细说明:
1>、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2>、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
3>当static用来修饰全局变量时,它就改变了全局变量的作用域,使其不能被别的程序extern,限制在了当前文件里,但是没有改变其存放位置,还是在全局静态储存区。
(4)const
被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。它可以修饰函数的参数、返回值,甚至函数的定义体。
作用:
1>修饰输入参数
a.对于非内部数据类型的输入参数,应该将“值传递”的方式改为“cons t引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
b.对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。
2>用const修饰函数的返回值
a.如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。
如对于:const char * GetString(void);
如下语句将出现编译错误:
char *str = GetString();//cannot convert from 'const char *' to 'char *';
正确的用法是:
const char *str = GetString();
b.如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。如不要把函数int GetInt(void) 写成const int GetInt(void)。
3>const成员函数的声明中,const关键字只能放在函数声明的尾部,表示该类成员不修改对象.
说明:
const type m; //修饰m为不可改变
示例:
typedef char * pStr; //新的类型pStr;
char string[4] = "abc";
const char *p1 = string;
p1++; //正确,上边修饰的是*p1,p1可变
const pStr p2 = string;
p2++; //错误,上边修饰的是p2,p2不可变,*p2可变
同理,const修饰指针时用此原则判断就不会混淆了。
const int *value; //*value不可变,value可变
int* const value; //value不可变,*value可变
const (int *) value; //(int *)是一种type,value不可变,*value可变
//逻辑上这样理解,编译不能通过,需要tydef int* NewType;
const int* const value;//*value,value都不可变
(5)volatile
表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。它可以适用于基础类型如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者类的所有成员都会被视为volatile.
14.7、异步IO和同步IO有什么区别?举例说明有几种(如read)?
异步IO当函数返回时不一定就完成了IO操作,而同步IO已经完成了。所以异步IO需要有一个事件,当IO完成时会设置此事件,调用者在事件上等待。一般说来,异步I/O是和同步I/O相比较来说的,如果是同步I/O,当一个I/O操作执行时,应用程序必须等待,直到此I/O执行完. 相反,异步I/O操作在后台运行,I/O操作和应用程序可以同时运行,提高了系统性能; 使用异步I/O会提高I/O流量,如果应用是对裸设备进行操作,这种优势更加明显
, 因此象数据库,文件服务器等应用往往会利用异步I/O,使得多个I/O操作同时执行.
8、32位系统中,出现结构字节对齐的问题和大小端的问题的避免?
指定对齐值:#pragma pack (value)时指定的对齐value
写一个函数判断系统是大端还是小端。若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1.
大端格式:在这种格式中,字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中
小端格式:与大端存储格式相反,在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节
联合体union的存放顺序是所有成员都从低地址开始存放。
Int checkCPU ()
{
Union w
{
Int a;
Char b;
}c;
c.a=1;
return (c.b==1);
}
14 如何在Release版本中查找以下问题,
a 内存泄漏
b 段错误导致非法操作
c 程序CPU占用100%
9、如何查出内存泄漏和非法操作的BUG(在Release版本下)?
检查window (release)下的内存泄漏
1、放置关键字assert()
2、生成map 文件。它并不往可执行文件exe 中添加任何东西,只是在编译的时候将各个函数入口地址记录在后缀为.map的文件中,程序崩溃的时候可以得到一个EIP地址,通过地址知道崩溃所在函数
3、可以设置断点,在希望设置断点的地方加入_ASM int 3
4、可以通过编译时的汇编程序看出
5、采用第三方工具
查找段错误导致的非法操作用
ptrace系统调用跟踪调试运行中的进程(truss、strace或ltrace的原理都是根据ptrace系统调用跟踪调试运行中的进程)用truss跟踪clint的系统调用来找出错误,clint是c++静态源码分析工具。通过ports安装好之后利用调试工具truss即可。
核心太与用户太的区别,x86如何转换。中断调用,从ring3转到ring0
现代的操作系统一般都有核心模式和用户模式之分,操作系统核心代码运行在核心模式下,具有较高的运行级别,具有较高的运行效率和较强的底层控制权力,系统硬件为其提供了尽可能多的内存保护以及其他措施,而用户进程则一般运行在用户模式下,其运行级别较低。
在x86平台下,核心态和用户态的区分主要是通过段选择子确定的,具体来说,在Linux环境下,核心态的代码段选择子为0x10,数据段选择子为0x18;而用户态的代码段选择子为0x23,数据段选择子为0x2B,因此核心态程序工作在ring0,而用户态程序工作在ring3。在用户模式下只能访问用户空间而在核心模式下可以访问系统空间和用户空间从用户态进入核心态的最常用的方法是在寄存器eax填一个功能码,然后执行int 2e。这有点像DOS时代的DOS和BIOS 系统调用。在NT架构中这种机制被称作system service。
Unix的启动顺序排序。
第一步:通过/boot/vm进行启动vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端
rc.sysinit
rc.d(里面的脚本)
rc.local
第四步:启动login登录界面login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/etc/bashrc
/root/.bashrc
/root/.bash_profile
对比平衡二叉树AVL和红黑树。哈希表
平衡二叉树和哈希表,哈希表的查找比较快,而且时间复杂度为O(1),但是它所占的空间比较大,而且插入和删除操作不够灵活,没有动态性。
红黑树算法的时间复杂度和AVL相同,但统计性能比AVL树更高
由于红黑树也是二叉查找树,它们当中每一个节点的比较值都必须大于或等于在它的左子树中的所有节点,并且小于或等于在它的右子树中的所有节点。这确保红黑树运作时能够快速的在树中查找给定的值。
问答
const 有什么用途?(请至少说明两种)
Const用了定义一个常量,
1、用在变量前面的时候可以避免变量被修改
2、用在函数声明部分允许const 的类对象成员访问const 成员函数,如果类的成员函数不会对数据成员进行修改的话最好把该函数定义为const类型,这样无论是const的类对象还是非const 的类对象都可以访问该函数
3、可以用来代替define ,define 只是简单的代替,但是const 还会进行类型检查。
怎么避免头文件重复包含
#ifndef H_HEADFILENAME
#define H_HEADFILENAME
文件内容….
#endif
#paragma once
C++拷贝构造函数和赋值运算符有那些不同和相同点。
同:都可以对对象的成员进行赋值
异:
拷贝构造函数首先是一个构造函数,它调用的时候产生一个对象,是通过参数传进来的那个对象来初始化,产生的对象。
赋值是把一个对象赋值给一个原有的对象,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。
Spinlock(自旋锁),mutex,(互斥)semaphore(信号量),vitical section(critical section)的作用与区别?
都是操作系统中内核同步实现数据访问,资源共享的作用
自旋锁是在cpu之间,只能运行一个对象,自旋锁可以在任何时刻防止多于一个的内核任务(进程)同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。
互斥只能运行一个对象,可以使一个进程下的线程间同步。也可以是不同进程间同步,互斥能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享
信号量可以运行多个对象,但是为线程间同步它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
临界区同一个进程下的线程间同步,只能运行一个对象。保证在某一个时间只有一个线程可以访问数据的方法
事件:通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作
正则表达式字符串匹配问题。
比较哈希表和平衡二叉树的特点,它们分别如用那些场合。
哈希表和平衡二叉树的适用场合
哈希表:哈希表查找速度比较快,可以时间复杂度为O(1)的情况下找到实现查找。但是要耗比较多的内存。所以比较适用于对查找速度要求比较高、且内存空间足够的时候
平衡二叉树:如果想在以后用二分法查找的时候查找速度比较快的话用建立平衡二叉树的方法()平衡二叉树支持动态的插入和查找,保证操作在O(height)时间,这就是完成了哈希表不便完成的工作,动态性。平衡二叉树/红黑树就是为了将查找的时间复杂度保证在O(logN)范围内。所以如果输入结合确定,所需要的仅仅是查询,则可以考虑使用哈希表,如果输入集合不确定,则考虑使用平衡二叉树/红黑树,保证达到最大效率。
recv函数如何在阻塞模式下没有收到数据就返回
Rev(buff,sizeof(buff),flag)
可以将rev的标志位定义为无需等待返回,不管其是否收到数据都立即返回,也可以定义为等待时间,如果超过等待时间还没有接受到数据就立即返回,
用锁效率低,有那些方法可以避免或减少锁的使用?
将表建立表级锁,减少锁数量的使用
Main函数中两个参数的作用
第一个形参argc是一个整型变量,第二个形参argv是一个指针数组,其元素指向字符型
数据。用带参数的main函数可以直接从命令行得到参数值(这些值是字符串),在程序运行
时,可以根据输入的命令行中的不同情况进行相应的处理。利用main函数中的参数可以使
程序从系统得到所需的数据,增加了处理问题的灵活性。
进程的几个基本状态:就绪、执行、阻塞
1.匹配"[10]:dddddd"和"[9]:abcdegf"但不匹配"[a]:xfdf"的正则表达式。\b\w{11}\b精确匹配只有11个字符的字符串
5 c++中虚函数如何定义,使用时应该注意什么?
虚函数是在类中被声明为virtual的成员函数,虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。
多态指同一个方法根据其所属的不同对象可以有不同的行为(根据自己理解,不知这么说是否严谨)。
8.有红、绿、蓝三色球分别3,2,1个。取任意两个不同颜色的球都会使它们变成第三种颜色的两个球。问最少取多少次,可使这些球都变成同一种颜色?
答:无论多少次,都不可以使这些球变成同一种颜色,
分析:
一、对于(R,R,R,G,G,B)即(3,2,1),有:
i. (R,G) ---> (B,B,B,R,R,G)即(3,2,1)
ii. (R,B) ---> (G,G,G,G,R,R)即(4,2)
iii. (G,B) ---> (R,R,R,R,R,G)即(5,1)
对于(G,G,G,G,R,R)即(4,2),有:
i. (R,G) ---> (G,G,G,B,B,R)即(3,2,1)
对于(R,R,R,R,R,G)即(5,1)有:
i. (R,G) ---> (R,R,R,R,B,B)即(4,2)
因此,只有三种状态(3,2,1), (4,2)和(5,1),不可能出现(6,0)这种情况。
6用C/C++编程,从1到100中取出10个不同的数,要求打印出所有可能的组合;#include
int source[100];
int dest[10]={0};
int index_source=0;
int index_dest=0;
int k=10;
int i=0;
int j=0;
void composition(int source[], int index_source, int dest[], int index_dest, int k) {
if(100-index_source==k)
{
for(i=0; i { cout< } for(i=index_source; i<100; i++) { cout<