阿里巴巴2017实习生笔试(一)

来源:互联网 发布:单片机中断程序编写 编辑:程序博客网 时间:2024/05/22 12:16

1.内联函数: 

A 项错误,因为使用 inline 关键字的函数只是用户希望它成为内联函数,但编译器有权忽略这个请求,比如:若此函数体太大,则不会把它作为内联函数展开的。

B 项错误,头文件中不仅要包含 inline 函数的声明,而且必须包含定义,且在定义时必须加上 inline 。【关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用】 
C 项错误, inline 函数可以定义在源文件中,但多个源文件中的同名 inline 函数的实现必须相同。一般把 inline 函数的定义放在头文件中更加合适。 
D 项正确,类内的成员函数,默认都是 inline 的。【定义在类声明之中的成员函数将自动地成为内联函数】

EF 项无意思,不管是 class 声明中定义的 inline 函数,还是 class 实现中定义的 inline 函数,不存在优先不优先的问题,因为 class 的成员函数都是 inline 的,加了关键字 inline 也没什么特殊的 


Tip: 只有当函数只有 10 行甚至更少时才将其定义为内联函数. 
定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用. 
优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联. 
缺点: 滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。 
结论: 一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用! 
另一个实用的经验准则: 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行). 

注意:有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联.通常, 递归函数不应该声明成内联函数.(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数).虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数. 


-inl.h文件: 
Tip: 复杂的内联函数的定义, 应放在后缀名为 -inl.h 的头文件中. 
内联函数的定义必须放在头文件中, 编译器才能在调用点内联展开定义. 然而, 实现代码理论上应该放在 .cc 文件中, 我们不希望 .h 文件中有太多实现代码, 除非在可读性和性能上有明显优势. 
如果内联函数的定义比较短小, 逻辑比较简单, 实现代码放在 .h 文件里没有任何问题. 比如, 存取函数的实现理所当然都应该放在类定义内. 出于编写者和调用者的方便, 较复杂的内联函数也可以放到 .h 文件中, 如果你觉得这样会使头文件显得笨重, 也可以把它萃取到单独的 -inl.h 中. 这样把实现和类定义分离开来, 当需要时包含对应的 -inl.h 即可。


2.


3.最初,甲杯子只有水,乙杯子只有酒精。 

最后,甲杯子恢复到最初的高度,只是混入了一些酒精。 

所以从乙杯子拿出多少酒精给甲杯子,乙杯子就有多少甲杯子的水。 

ps:题目已经假设 酒精和水混合后等于两者容量之和 


4.

char *a[]={"work","at","alibaba"};char **pa=a;pa++;printf("%s",*pa);
首先对编译器来说没有数组这一概念,数组都被看成指针,所以a[ ]就是*a,那么就是**a换成了**pa,pa即是a,换个名字而已,根据数组的++,也就是取a[1][ ]的值,即“at”。


5

6 后序遍历(略)

7. TCP/IP协议三次握手与四次握手

 TCP建立连接时,首先客户端和服务器处于close状态。然后客户端发送SYN同步位,此时客户端处于SYN-SEND状态,服务器处于lISTEN状态,当服务器收到SYN以后,向客户端发送同步位SYN和确认码ACK,然后服务器变为SYN-RCVD,客户端收到服务器发来的SYN和ACK后,客户端的状态变成ESTABLISHED(已建立连接),客户端再向服务器发送ACK确认码,服务器接收到以后也变成ESTABLISHED,然后服务器客户端开始数据传输。

服务端开始监听之后,就处于listen状态,然后客户端发送的syn才能被服务端收到。服务端接收到syn之后,发送syn和ack给客户端,自身的状态就变成了syn_recv。处在SYNC_RECV的TCP连接称为半连接,并存储在内核的半连接队列中,在内核收到对端发送的ack包时会查找半连接队列,并将符合的requst_sock信息存储到完成三次握手的连接的队列中,然后删除此半连接。


8.假设n进制下,下面等式城市,n为多少? 240*12=2880. 

A 19 B18 C 17 D 16 E 15 F以上都对

假设为n进值则 [2*(n^2)+4*(n^1)+0] * [1*n+2]=2*(n^3)+8*(n^2)+8*(n^1)  化简后为恒等式 ,所以n为任一值。


9. 关于系统调用的描述,错误的是:(B)

A 系统调用把应用程序的请求传输给系统内核执行;

B 系统调用中被调用的过程运行在“用户态”中;

C 利用系统调用能够得到操作系统提供的多种服务

D 是操作系统提供给编程人员的接口

E 系统调用给用户屏蔽了设备访问的细节

F 系统调用保护了一些智能在内核模式执行的操作指令

分析:用户空间与系统空间所在的内存区间不一样,同样,对于这两种区间,CPU的运行状态也不一样。 在用户空间中,CPU处于"用户态";在系统空间中,CPU处于"系统态"。


系统调用概述 系统调用的实现原理

        计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同时运行的多个进程都需要访问这些资源,为了更好的管理这些资源进程是不允许直接操作的,所有对这些资源的访问都必须有操作系统控制。也就是说操作系统是使用这些资源的唯一入口,而这个入口就是操作系统提供的系统调用(System Call)

        系统调用是属于操作系统内核的一部分的,必须以某种方式提供给进程让它们去调用。CPU可以在不同的特权级别下运行,而相应的操作系统也有不同的运行级别,用户态和内核态。运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。显然,属于内核的系统调用一定是运行在内核态下,但是如何切换到内核态呢?

        答案是中断。操作系统一般是通过中断从用户态切换到内核态。中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情。比如,在x86机器上可以通过int指令进行软件中断,而在磁盘完成读写操作后会向CPU发起硬件中断。

        中断有两个重要的属性,中断号和中断处理程序。中断号用来标识不同的中断,不同的中断具有不同的中断处理程序。在操作系统内核中维护着一个中断向量表(Interrupt Vector Table),这个数组存储了所有中断处理程序的地址,而中断号就是相应中断在中断向量表中的偏移量。

        一般地,系统调用都是通过中断实现的,比如,Linux下中断号0x80就是进行系统调用的。



系统调用的意义:系统调用过程
      操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用:1.把用户从底层的硬件编程中解放了出来;2.极大地提高了系统的安全性使用户程序具有可移植性;用户程序与具体硬件已经被抽象接口所替代。

操作系统提供的API和系统调用的关系:
    API(应用程序编程接口)和系统调用:应用编程接口和系统调用是不同的:1.API只是一个函数定义;2.系统调用通过软中断向内核发出了一个明确的请求。
     Libc库定义的一些API引用了封装例成,唯一目的就是发布系统调用:1.一般每个系统调用对应一个封装例程;2.库函数再用这些封装例程定义出给用户的API(把系统调用封装成很多歌方便程序员使用的函数,不是每个API都对应一个特定的系统调用)
     API可能直接提供用户态的服务 如:一些数学函数 1.一个单独的API可能调用几个系统调用2.不同的API可能调用了同一个系统调用返回:大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用-1在多数情况下表示内核不能满足进程的请求,Libc中定义的errno变量包含特定的出错码;

10.select 和 epoll效率差异的原因:select采用轮询方式处理连接,epoll是触发式处理连接。 

Select: 
1).Socket数量限制:该限制可操作的Socket数由FD_SETSIZE决定,内核默认32*32=1024. 
2).操作限制:通过遍历FD_SETSIZE(1024)个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。 

Epoll 
1).Socket数量无限制:该模式下的Socket对应的fd列表由一个数组来保存,大小不限制(默认4k)。 
2).操作无限制:基于内核提供的反射模式,有活跃Socket时,内核访问该Socket的callback,不需要遍历轮询。 

但当所有的Socket都活跃的时候,所有的callback都被唤醒,会导致资源的竞争。既然都是要处理所有的Socket, 
那么遍历是最简单最有效的实现方式。 


select 

select能监控的描述符个数由内核中的FD_SETSIZE限制,仅为1024,这也是select最大的缺点,因为现在的服务器并发量远远不止1024。即使能重新编译内核改变FD_SETSIZE的值,但这并不能提高select的性能。 
每次调用select都会线性扫描所有描述符的状态,在select结束后,用户也要线性扫描fd_set数组才知道哪些描述符准备就绪,等于说每次调用复杂度都是O(n)的,在并发量大的情况下,每次扫描都是相当耗时的,很有可能有未处理的连接等待超时。 
每次调用select都要在用户空间和内核空间里进行内存复制fd描述符等信息。 


poll 

poll使用pollfd结构来存储fd,突破了select中描述符数目的限制。 
与select的后两点类似,poll仍然需要将pollfd数组拷贝到内核空间,之后依次扫描fd的状态,整体复杂度依然是O(n)的,在并发量大的情况下服务器性能会快速下降。 


epoll 

epoll维护的描述符数目不受到限制,而且性能不会随着描述符数目的增加而下降。 
服务器的特点是经常维护着大量连接,但其中某一时刻读写的操作符数量却不多。epoll先通过epoll_ctl注册一个描述符到内核中,并一直维护着而不像poll每次操作都将所有要监控的描述符传递给内核;在描述符读写就绪时,通过回掉函数将自己加入就绪队列中,之后epoll_wait返回该就绪队列。也就是说,epoll基本不做无用的操作,时间复杂度仅与活跃的客户端数有关,而不会随着描述符数目的增加而下降。 
epoll在传递内核与用户空间的消息时使用了内存共享,而不是内存拷贝,这也使得epoll的效率比poll和select更高。


11. 用杯子量水问题通用解法

有无限的水源,一个5L无刻度桶和一个7L无刻度桶,则只利用这两个无刻度桶,将不能获得()L水

A、2 B3C、6D、8E、11F、以上均能获得

12.7的2014次方这个整数的十位数字数是()

A、0 B、1 C、3D、4E、5F、7

分析:计算7^2014%100=(7^2*7^2012)%100=7^2%100*(7^2012%100)%100=49%100*(7^2012%100)%100=49%100*(2401^503%100)%100=49%100*(2401%100)^503%100=49%100*1%100=49,所以7^2014的最后两位是49。

求结果的十位数,即求结果除以100的余数 7^2014mod100=(49^1006×49)mod100=(2401^503×49)mod100=(2401mod100)^503×49mod100=1×49=49,十位数为4

13.在100-999这900个自然数中,若将组成这个数的三个数字认为是三条线段的长度,那么是三条线段组成一个等腰三角形(包括等边)的共有()个. A、45B、91 C、121D、142E、156F、165

先考虑等边三角形情况 
则a=b=c=1,2,3,4,5,6,7,8,9,此时n有9个 
再考虑等腰三角形情况,若a,b是腰,则a=b 
当a=b=1时,c<a+b=2,则c=1,与等边三角形情况重复; 
当a=b=2时,c<4,则c=1,3(c=2的情况等边三角形已经讨论了),此时n有2个; 
当a=b=3时,c<6,则c=1,2,4,5,此时n有4个; 
当a=b=4时,c<8,则c=1,2,3,5,6,7,有6个; 
当a=b=5时,c<10,有c=1,2,3,4,6,7,8,9,有8个; 
由加法原理知n有2+4+6+8+8+8+8+8=52个 
同理,若a,c是腰时,c也有52个,b,c是腰时也有52个 
所以n共有9+3×52=165个 

14.关于线性表 
循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的 指针 域指向 头结点 ,整个链表形成一个环。 
(1)单循环链表——在单链表中,将终端结点的指针域NULL改为指向表头结点或开始结点即可。 
(2)多重链的循环链表——将表中结点链在多个环上。 
 

队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表; 
 

栈(stack)在计算机科学中是限定仅在栈顶进行插入或删除操作的线性表。 
 

关联数组”是一种具有特殊索引方式的数组。不仅可以通过整数来索引它,还可以使用字符串或者其他类型的值(除了NULL)来索引它。 关联数组和数组类似,由以名称作为键的字段和方法组成。 它包含标量数据,可用索引值来单独选择这些数据,和数组不同的是, 关联数组的索引值不是非负的整数而是任意的标量。这些标量称为Keys,可以在以后用于检索数组中的数值。 关联数组的元素没有特定的顺序,你可以把它们想象为一组卡片。每张卡片上半部分是索引而下半部分是数值。 

链表(Linked list)是一种常见的基础数据结构,是一种线性表,是一种物理存储单元上非连续、非顺序的存储结构。双向链表也叫 双链表 ,是链表的一种,它的每个数据结点中都有两个 指针 ,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向 循环链表 。

15.下面的哪种排序算法在算复杂度平均不是O(nlogn)的?

A、快速排序B、桶排序 C、合并排序D、二叉树排序树排序E、堆排序

分析:桶排序的平均时间复杂度为线性的O(N+C),其中C=N*(logN-logM)。如果相对于同样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。 当然桶排序的空间复杂度 为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。


16、某创业团队的一个很大的办公室(障碍和遮挡可以忽略)里有一个WIFI源,有1个工位由于距离WIFI源太远而接收不到信号.为了提高该工位的联网质量,准备在工位和WIFI信号源之间安装一个无线AP(相当于中继的,可以中转该工位上的计算机和WIFI之间的信号).只考虑从WIFI发出的信号,如果AP离WIFI源太近,就起不到中继的作用,如果AP离工位太远则可能连不上WIFI.因此AP有一个最佳的安装位置,那么关于AP最佳安装位置的说法正确的是(B)

A、如果WIFI源功率越大,那么AP最佳安装位置越靠近WIFI源 B、如果WiFi源功率越大,那么AP最佳的安装位置越靠近工位 

C、WIFI源功率和AP最佳安装位置无关. E、AP最佳安装位置在工位和WIFI信号源连线之外

F、AP最佳安装位置在工位和WIFI信号源连线中点 G、以上说法都不对


17、有100个金币,分给10个人.第一个金币等概率地分给10个人之一.之后的每一个金币分配给第K个人的概率正比于这个人已经持有的金币数+1.在这样的分配机制下,关于每个人最终的金币个数的分布的说法错误的是(B)

A、每个人得到的金币的个数的期望是相等的B、每个人的金币个数接近均匀分布 C、第一个金币给哪个人,哪个人的最终金币个数的期望就会更大D、在中间的某个阶段金币个数越多的人,未来获得金币的可能性越大

18、在自由交换的情况下,只考虑偏好,小张用自己的小刀换了小王的橡皮.关于这个交换以下说法错误的是:F

A、小张觉得橡皮比小刀更好B、小王觉得小刀比橡皮更好 C、小张和小王总的财富里没有发生变化D、小张和小王的效用值增加了E、如果把小王换成小吴,那么这个交换可能就不会发生F、小刀和橡皮等值

19、struct st { int *p; int i; char a; }; int sz=sizeof(struct st);在64位处理器上运行后sz的值是什么?C

A、24 B、20 C、16 D、14 E、13 F、12

20 略

21、下面关于一个类的静态成员描述中,不正确的是(C)

A、静态成员变量可被该类的所有方法访问 B、该类的静态方法只能访问该类的静态成员函数 C、该类的静态数据成员变量的值不可修改 D、子类可以访问父类的静态成员F、静态成员无多态特性

分析:类的静态成员属于整个类 而不是某个对象,可以被类的所有方法访问,子类当然可以父类静态成员;

静态方法属于整个类,在对象创建之前就已经分配空间,类的非静态成员要在对象创建后才有内存,所有静态方法只能访问静态成员,不能访问非静态成员;

静态成员可以被任一对象修改,修改后的值可以被所有对象共享。 

22、给定的一个长度为N的字符串str,查找长度为P(P<N)的字符串在str中的出现次数.下面的说法正确的是()

正确答案:D

A、不存在比最坏时间复杂度O(NP)好的算法 B、不存在比最坏时间复杂度O(N^2)好的算法 

C、不存在比最坏时间复杂度O(P^2)好的算法 D、存在最坏时间复杂度为O(N+P)的算法

E、存在最坏时间复杂度为O(log(N+P))的算法 F、以上都不对

 分析:朴素匹配算法 时间复杂度O((N-P+1)*P) ,KMP匹配算法 时间复杂度为O(N+P)  

23、某体校选择校服,每套校服都包括短袖运动衫,长袖运动衫,厚外套,运动长裤和运动短裤组成.每种运动服有个备选方案.老师请了部分学生来挑选自己喜欢的校服.结果发现任意3个学生都至少在一种运动服上选择互不相同,那么老师最多邀请了()名学生参加挑选.

正确答案:B 

A、7 B、8 C、9D、10 E、11F、12

“每套校服都包括短袖运动衫,长袖运动衫,厚外套,运动长裤和运动短裤组成. 每种运动服有个备选方案 ”,我觉得这句意思应该是校服包含有 两 种运动服,每种运动服有 两 种选择。所以学生总共有 4 中不同的选择方案。 
“任意3个学生都至少在一种运动服上选择互不相同”,可以转换为 任意3个学生都至少有两种选择方案。题目又要求 求 最多的人数,那么进一步我们可以说,每三个学生中就有两个选择同一种方案。最大化的话就是每种方案有两个人选,那么最大人数必然是方案数目乘以2,也就是8. 
在讨论下有同学认为运动服选择方案有32种的。如果有32中,最大人数不应该至少超过32吗,因为如果32人中每人选不同的方案,这时候也满足 任意3个学生都至少在一种运动服上选择互不相同,所以我认为不可能有32中方案。 

24、有4副相同的牌,每副牌有4张不同的牌.先从这16张牌中,随机选4张出来.然后,在这4张牌中随机选择一张牌,然后把抽出的一张放回3张中,再随机选择一张牌.与上次选出的牌一样的概率是()

正确答案:C 

A、1/4 B、1/3 C、2/5 D、1/2 E、2/3 F、3/4

 直接看第二次抽样即可,与第一次抽的是同一张牌的概率是1/4,不同张的概率是3/4,同一张的话肯定是一样,不同张的时候如果抽中一样的牌的概率是3/15,所以答案是1/4+3/4 * 3/15 = 2/5 


参考:

阿里巴巴2017实习生笔试题(一)

阿里巴巴2017实习生笔试题(一)

阿里笔试题2017重点

阿里巴巴2017实习生笔试(一)