2021年奇虎360技术岗(程序员)面试题

小编:管理员 2010阅读 2021.06.24

第1题:



Class A;

Class B;

void F() {

        A a;

        B b;

}

  

在函数F中,本地变量a和b的构造函数(constructor)和析构函数(destructor)的调用顺序是:

         

A  b构造 a构造 a析构 b析构

B  a构造 a析构 b构造 b析构

C  b构造 a构造 b析构 a析构

D  a构造 b构造 b析构 a析构




按变量声明顺序构造对象,然后入栈

按相反顺序出栈,析构对象。


第2题:


 假定指针变量p定义为“int *p=new int(100);”,要释放p所指向的动态内存,应使用语句( )

 

A  delete p;

B  delete *p;

C  delete &p;

D  delete []p;



 A


第3题:


 选择填空:


#include

void test(void *data) {

    unsigned int value = (此处应填入)

    printf("%u", value);

}

using namespace std;

int main() {

    unsigned int value = 10;

    test(&value);

    return 0;

}

 

A  *data

B  (unsigned int)(*data)

C  (unsigned*)data

D  *((unsigned int *)data)




 D

解释: 

  void

 注意,参数类型是void,     所以先要进行指针转换:(unsigned    int *)然后再取值。



第4题:


 在C++, 下列哪一个可以做为对象继承之间的转换

    

A  static_cast

B  dynamic_cast

C  const_cast

D  reinterpret_cast



 B
dynamic_cast 动态转换


第5题:


 如果进栈序列为e1,e2,e3,e4,则不可能的出栈序列是( )

     

A  e2,e4,e3,e1

B  e4,e3,e2,e1

C  e1,e2,e3,e4

D  e3,e1,e4,e2



 D

如果e3第一个出栈,拿下一个应该是e4或者e2,但绝不可能是e1


第6题:


 若某二叉树的前序遍历访问顺序是abdgcefh,中序遍历访问顺序是dgbaechf,则其后序遍历的结点访问顺序是( )

 

A  gdbehfca

B  hcdeabf

C  fdcehgba

D  gdbehcfa



 A

根据前序和中序序列画出二叉树的结构,写出其后序序列即可。


第7题:


 用二分法查找长度为10的、排好序的线性表,查找不成功时,最多需要比较多少次?

  

A  3

B  4

C  5

D  6



 B

8<10<16,而log16=4


第8题:


 以下程序是用辗转相除法来计算两个非负数之间的最大公约数:


long long gcd(long long x, long long y) {

    if (y == 0)

        return x;

    else

        return gcd(y, x % y);

}

  

我们假设x,y中最大的那个数的长度为n,x>y,基本运算时间复杂度为O(1),那么该程序的时间复杂度为( )

 

A  O(1)

B  O(logy)

C  O(n)

D  O(x)




 求最大公约数的最常用的算法是欧几里得算法,也称为辗转相除法.
问题定义为求i和j的最大公约数gcd(i,j),其中i和j是整数,不妨设i>j.
算法可以递归的表示:
1.如果j能整除i,那么gcd(i,j)=j;
2.j不能整除i,令r=i%j,那么gcd(i,j)=gcd(j,r).
使用C语言实现:

int gcd(int i, int j)

{

    int r = i % j;

    return r == 0 ? j : gcd(j, r);

}

正确性分析:
算法的步骤1,显然成立(最大公约数定义).关键是要证明步骤2.
设d是i和j的最大公约数,
那么i=md,j=nd,m和n互质(否则d不是最大公约数).
由r=i%j可以得到i=kj+r,k=?m/n?,k≥1(我们前面假设过i>j).
把i=md,j=nd代入得到
md=knd+r
那么
r=(m-kn)d
m-kn和m也是互质的.
所以得到d是j和r的最大公约数.

时间复杂度分析:
逆着看该算法,最后的余数是0,倒数第二次余数是d,倒数第三次是kd,k>1…
由于组成了一个数列,{0,d,kd,nkd+d,…}
数列的n项加上n+1项,比n+2项要小,所以比斐波纳契数列增长的要快.
我们已知斐波纳契数列增长速度是指数,那么待分析的数列也是指数增长.
设欧几里得算法需要k次,那么j=O(2^k),则k=O(lg j).

所以欧几里得算法求最大公约数的时间复杂度是对数量级的,速度非常快.


第9题:


 一棵有124个叶节点的完全二叉树,最多有( )个节点。

 

A  247

B  248

C  249

D  250



 B

n0 = n2 + 1,于是度为2的结点个数123个完全二叉树中度为1结点个数最多1个因此该完全二叉树中结点最多有123  + 1 + 124 = 248个


第10题:


 链表不具备的特点是( )

 

A  可随机访问任何一个元素

B  插入、删除操作不需要移动元素

C  无需事先估计存储空间大小

D  所需存储空间与线性表长度成正比



 A

不同于寻秩访问的数组,链表无法实现随机访问任何一个元素O(1), 访问时需要遍历链表直到访问到该元素。


第11题:


 下列排序算法中,在待排序数据有序的情况下,花费时间最多的是( )

 

A  快速排序

B  希尔排序

C  冒泡排序

D  堆排序



 A

待排序数据有序就是快排的最差情况,此时的时间复杂度是O(n   2   )


第12题:


 有 1000 个无序的整数,希望使用最快的方式找出前 50 个最大的,最佳的选择是( )

A  冒泡排序

B  基数排序

C  堆排序

D  快速排序



 C

找出若干个数中最大/最小的前K个数,用堆排序是最好的

找最大数,用小根堆

找最小数,用大根堆


第13题:


 下面哪个不是用来解决哈希表冲突的开放地址法?

 

A  线性探测法

B  线性补偿探测法

C  拉链探测法

D  随机探测法



 C

拉链探测法,开放定址法区分为线性探查法、线性补偿探测法、随机探测等。


第14题:


 下列数最大的是( )。括号内为数字,括号外为进制。

 

A  (10010101)2

B  (227)8

C  (96)16

D  (143)10



 B


第15题:


 在CPU和内存之间增加cache的作用是(  )

     

A  提高内存稳定性

B  解决内存速度低于CPU的性能问题

C  增加内存容量

D  增加内存容量且加快存取速度



 B

这是存储器分层部分的内容,可以参考《深入理解计算机系统》


第16题:


 假设整数0x12345678 存放在内存地址0x0开始的连续四个字节中 (即地址0x0到 0x3). 那么在以Little Endian字节序存储的memory中,地址0x3的地方存放的字节是:

A  0x12

B  0x34

C  0x56

D  0x78



 A

a) Little-Endian就是低位字节排放在内存的低地址端,   高位字节排放在内存的高地址端。

b)  Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

c)   网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

如果是 Little-Endian:0x0-0x3内存分别存放的是:0x78、0x56、0x34、0x12;       

如果是        Big-Endian     :0x0-0x3内存分别存放的是:0x12、0x34、0x56、0x78;          


第17题:


 将逻辑代码:

if (x % 2) {

    return x - 1;

} else {

    return x;

}

  

用表达式:return x & -2; 替代,以下说法中不正确的是( )

   

A  计算机的补码表示使得两段代码等价

B  用第二段代码执行起来会更快一些

C  这段代码只适用于x为正数的情况

D  第一段代码更适合阅读

 



 C


第18题:


 代码生成阶段的主要任务是( )

         

A  把高级语言翻译成汇编语言

B  把高级语言翻译成机器语言

C  把中间代码变换成依赖具体机器的目标代码

D  把汇编语言翻译成机器语言



 C

编译程序的工作过程一般划分为五个阶段:词法分析、语法分析、语义分析与中间代码产生、优化、目标代码生成。所以选C 。


第19题:


 后缀式 ab+cd+/可用表达式( )来表示

 

A  a+b/c+d

B  (a+b)/c+d

C  a+b/(c+d)

D  (a+b)/(c+d)



 D

后缀表达式不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行,不再考虑运算符的优先规则。
 最后一个操作是'/',而且前面是一个+操作,后面没有操作,可知/操作最后进行,由此可得,另外两个操作数是a+b和c+d,因此表达式为D (a+b)/(c+d)


第20题:


 以下关于函数调用的说法哪个是正确的?

      

A  传值后对形参的修改会改变实参的值

B  传地址后实参和形参指向不同的对象

C  传引用后形参和实参是不同的对象

D  以上都不对



 D

解释:传地址和传引用,形参,实参指向同一对象,修改形参会影响实参

传值则相反,修改形参不会影响实参


第21题:


 一个合法的 360 账户名称要求如下:是一个合法的邮箱地址,如 [email protected];邮箱前缀的长度为 [ 4, 16 ] 个字符;邮箱前缀必须以字母开头,字母或数字结尾;邮箱前缀可以包括字母、数字、下划线。请问如下正则表达式中,哪一个能正确校验用户名的合法性:

 

A  \w[0-9a-zA-Z_]{3,15}\@\w+.([-.]\w+)*

B  [a-zA-Z]\w{3,15}@\w+.\w*

C  [a-zA-Z]\w{2,14}[0-9a-zA-Z]\@\w+([-.]\w+)*

D  [a-zA-Z]\w{2,14}[0-9a-zA-Z]@\w



 C

邮箱前缀必须以字母开头,字母或数字结尾    这句话 排除A

邮箱中肯定有“.”     ,D中没有 排除D

邮箱前缀可以包括字母、数字、下划线    ,B中 没有数字,排除B


所以答案是C


第22题:


 词法分析器用于识别( )

  

A  句子

B  句型

C  单词

D  生产式



 C

思路:参见维基百科:http://zh.wikipedia.org/zh/%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90

词法分析器是将源文件识别为单词,对单词进行分类


第23题:


 在下列说法中,哪个是错误的( )

 

A  若进程A和进程B在临界段上互斥,那么当进程A处于该临界段时,它不能被进程B中断

B  虚拟存储管理中采用对换(swapping)策略后,用户进程可使用的存储空间似乎增加了

C  虚拟存储管理中的抖动(thrashing)现象是指页面置换(page replacement)时用于换页的时间远多于执行程序的时间

D  进程可以由程序、数据和进程控制块(PCB)描述



 C

c选项中:是请求分页虚拟存储管理。
当需要执行否条的指令或使用某个数据而发现他们不再内存中时候,会产生缺页异常。
系统从磁盘中把此指令或数据所在的页面装入。缺页异常是由硬件所产生的一种特殊终端信号,其中当中断率较高时,整个系统的页面调度非常频繁造成大部分时间都花费在来回调度上,而不是执行任务,这种现象叫做“抖动”。——《操作系统》


第24题:


 操作系统采用分页式存储管理(PAGING)方法,要求( )

  

A  每个进程拥有一张页表,且进程的页表驻留在内存中

B  每个进程拥有一张页表,但只要执行进程的页表驻留在内存中,其他进程的页表不必驻留在内存中

C  所有进程共享一张页表,以节约有限的内存空间,但页表必须驻留在内存中

D  所有进程共享一张页表,只有页表中当前使用的页面必须驻留在内存中,以最大限度地节约有限的内存空间



 B 

在内核中 所有进程都是用一个页目录表,每个进程都有自己的页表 


第25题:


 计算机操作系统出现死锁的原因是什么?

       

A  资源数大大少于进程数,或进程同时申请的资源数大大超过资源总数

B  有多个封锁的进程同时存在

C  一个进程进入死循环

D  若干进程因竞争资源而无休止的等待着其他进程释放已占有的资源



 D

死锁的原因在于进程在等待其它进程占有的某些资源,而自身的资源又被其它进程等待着,造成了死循环。


第26题:


 进程间通讯的方式中哪种的访问速度最快?

    

A  管道

B  消息队列

C  共享内存

D  套接字



 C

常见进程间通信方式的比较:

管道:速度慢,容量有限
消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题。
信号量:不能传递复杂消息,只能用来同步
共享内存区:能够很容易控制容量,速度快 ,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了一块内存的。


第27题:


 TCP的关闭过程,说法正确的是( )

A  处于TIME_WAIT状态的连接等待2MSL后真正关闭连接

B  对一个established状态的TCP连接,在调用shutdown函数之前调用close接口,可以让主动调用的一方进入半关闭状态

C  主动发送FIN消息的连接端,收到对方回应ack之前不能发只能收,在收到对方回复ack之后不能发也不能收,进入CLOSING状态

D  在已经成功建立连接的TCP连接上,任何情况下都不允许丢失数据。



 A
 TIME_WAIT状态下发送的ACK丢失,服务器端的LAST_ACK时刻设定的重传定时器超时,发送重传的FIN,很不幸,这个FIN也丢失,主动关闭方在   TIME_WAIT状态等待2MSL没收到任何报文段,进入CLOSED状态,当此时被动关闭方并没有收到最后的ACK。所以即使要主动关闭方在 TIME_WAIT状态下停留2MSL,也不一定表示四次握手关闭就一定正常完成


第28题:


 linux中调用write发送网络数据返回n(n>0)表示(  )

   

A  对端已收到n个字节

B  本地已发送n个字节

C  系统网络buff收到 n个字节

D  系统调用失败



 B

已发送,但不保证对方收到

write函数的返回值的含义本来就是这样  


第29题:


 HTTP 应答中的 500 错误是:

     

A  服务器内部出错

B  文件未找到

C  客户端网络不通

D  没有访问权限



 A

403:禁止访问;

404:找不到该页面;

503:服务器繁忙;

500:内部服务器访问出错。


第30题:


 下列关于 Android 数字签名描述错误的是:

     

A  所有的应用程序都必须有数字证书,Android系统不会安装一个没有数字证书的应用程序

B  Android程序包使用的数字证书可以是自签名的,不需要一个权威的数字证书机构签名认证

C  如果要正式发布一个Android程序,可以使用集成开发工具生成的调试证书来发布。

D  数字证书都是有有效期的,Android只是在应用程序安装的时候才会检查证书的有效期。如果程序已经安装在系统中,即使证书过期也不会影响程序的正常功能。



 C

必须要使用一个合适的私钥生成的数字证书来给程序签名,而不能使用adt插件或者ant工具生成的调试证书来发布。


第31题:


 二、填空题

小支欲用积分兑换安仔娃娃。兑换的规则是10积分可以兑一个安仔并返还5积分。小支有200积分,最多可以兑到         ________个安仔?(假设可以借积分)



 40

借我200积分换成40个按仔,返回的200积分还给你


第32题:


 五对夫妇甲,乙,丙,丁,戊举行家庭聚会 每一个人都可能和其他人握手, 但夫妇之间绝对不握手. 聚会结束时, 甲先生问其他人: 各握了几次手? 得到的答案是: 0,1,2,3,4,5,6,7,8. 试问: 甲太太握了__________ 次手?



 4

甲太太握了4次手。 首先,可以断言握了8次手的人和握了0次手的人是一家人。因为一个人握了0次手,说明他(她)没有和其他任何人握手,而握了8次手的人握了别家的所有人的手,如果握了8次手的这个人和握了0次手的这个人不是一家人,握了8次手的这个人就必然握过握了0次手的人,那么,握了0次手的人就被握了8次手的人握了1次,这就矛盾了。 其次,可以断言握了1次手的人和握了7次手的人是一家人。因为现在大家都至少握过一次手了(和握过8次手的那个人握的),所以握过7次手的人必须和除了第一家和自己家的所有人握手,而握过1次手的人已经不能再和任何人握手了,因此,他们只能是一家人。 同理,握了2次手的人和握了6次手的人是一家人,握了3次手的人和握了5次手的人是一家人,握了4次手的是最后一家人。 现在来看,如果甲太太握了0次手,那么甲先生必然要握8次手,而且没有其他人可以握8次手,但是,甲先生是提问的人,因此,他没有握8次手,因此,甲太太也就不可能握0次手。 同理,甲太太也不可能握了1,2,3,5,6,7,8次手。 甲太太只可能握了4次手。


第33题:


 赛马,有25匹马,每次只能5匹马进行比赛,比赛只能得到5匹马之间的快慢程度,而不是速度,请问,最少要比_____________次,才能获得最快的前3匹马?



 7

25匹马,速度都不同,但每匹马的速度都是定值。现在只有5条赛道,无法计时,即每赛一场最多只能知道5匹马的相对快慢。问最少赛几场可以找出25匹马中速度最快的前3名? 每匹马都至少要有一次参赛的机会,所以25匹马分成5组,一开始的这5场比赛是免不了的。接下来要找冠军也很容易,每一组的冠军在一起赛一场就行了 (第6场)。最后就是要找第2和第3名。我们按照第6场比赛中得到的名次依次把它们在前5场比赛中所在的组命名为A、B、C、D、E。即:A组的冠军是第 6场的第1名,B组的冠军是第6场的第2名……每一组的5匹马按照他们已经赛出的成绩从快到慢编号: A组:1,2,3,4,5 B组:1,2,3,4,5 C组:1,2,3,4,5 D组:1,2,3,4,5 E组:1,2,3,4,5 从现在所得到的信息,我们可以知道哪些马已经被排除在3名以外。只要已经能确定有3匹或3匹以上的马比这匹马快,那么它就已经被淘汰了。可以看到, 只有上表中粗体的那5匹马是有可能为2、3名的。即:A组的2、3名;B组的1、2名,C组的第1名。取这5匹马进行第7场比赛,第7场比赛的前两名就是 25匹马中的2、3名。故一共最少要赛7场。


第34题:


 店主销售电话卡,他以60元的价格各销售了两张。其中一张是赚了20%,另一张是亏了20%。 请问他总共赚了_____________钱(亏了的话请用负数表示)?



 -5

有两个电话卡,a(赚钱)和b(亏钱),a的成本为x,x(1+0.2)=60,所以x=50,a赚10元

b的成本为y,y(1-0.2)=60,y=75,b亏15元

所以综合起来 亏5元


第35题:


 三、问答题

在审计某一开源项目的代码时,假设有下面一个foo()子函数的实现。 从安全的角度看,会存在安全漏洞吗?有的话,请
(1)描述漏洞细节,
(2)说明可以利用的方法,
(3) 还有该怎么修补漏洞。没有的话,也请说明为什么。

int foo((void*funcp)()) {

    char *ptr = pointer_to_an_array;

    char buf[128];

    gets(buf);

    strncpy(ptr,buf,8)

    (*funcp)();

}



 ptr 定义在 buf 的前面。在栈上开变量的话,后开的内存地址较小,也就是 ptr 是恰好接在 buf 数组的后面。所以如果数组越界就可以修改 ptr 指向的内存地址。


第36题:


 写一个函数找出一个整数数组中,第二大的数



 

int Find_Second_Max(int data[],int n)

{

    if(n<2) return -1;

    int max_num = max(data[0],data[1]);

    int sec_num = min(data[0],data[1]);

    for(int i=2;i

    {

        if(data[i]>=max_num);

        {

            sec_num = max_num;

            max_num = data[i];

        }

        else if(data[i] > sec_num)//排除等于情况

            sec_num = data[i];

    }

    return sec_num;

}


关联标签: