因为是第一次找实习,还是菜鸟,所以我采取的是海投战术,多笔试多面试多增长经验。以下是我这阵子的面试经历(部分答案是事后在网上找的),因为已经找到了满意的实习,所以还有其他公司的笔试面试也就不参加了。
阿里一面(处女电话面):
1.char s[10] = {0}; s[0] = ‘1’;问sizeof、strlen。
2.一个类,有普通成员变量int *a,有普通成员函数,有虚函数,求sizeof。普通成员函数存在哪?
一般情况下,同一个类的不同对象,共享成员函数的代码。成员函数的代码存储在对象空间之外。换句话说,成员函数的代码,都不占据对象的存储空间。
不同的对象使用的是同一个函数代码段,它怎么能够分别对不同对象中的数据进行操作呢?
原来C++为此专门设立了一个名为this的指针,用来指向不同的对象。需要说明:
不论成员函数在类内定义还是在类外定义,成员函数的代码段都用同一种方式存储。
不要将成员函数的这种存储方式和inMne(内置)函数的概念混淆。不要误以为用inline声明(或默认为inline)的成员函数,其代码段占用对象的存储空间,而不用 inline声明的成员函数,其代码段不占用对象的存储空间。不论是否用inline声明,成员函数的代码段都不占用对象的存储空间。用inline声明的作用是在调用该函数时,将函数的代码段复制插人到函数调用点,而若不用inline声明,在调用该函数时,流程转去函数代码段的人口地址,在执行完该函数代码段后,流程返回函数调用点。inline与成员函数是否占用对象的存储空间无关,它们不属同一个问題,不应搞混。
应当说明,常说的“某某对象的成员函数”,是从逻辑的角度而言的,而成员函数的存储方式,是从物理的角度而言的,二者是不矛盾的。
3.浅拷贝、深拷贝,实现string的深拷贝
4.判断两个链表是否有交叉 ,如果是则求出交叉结点。
1. 问题描述
给定两个单链表,查找这两个单链表的交叉节点。例如:链表listA为:a1→a2→c1→c2→c3,链表listB为:b1→b2→b3→c1→c2→c3。那么这两个的第一个交叉节点为c1。
-
方法与思路
首先,观察一下交叉节点的特点。如果两个链表有交叉节点的话,那么这个交叉节点之后的其他节点都是相同的,也就是说两个链表的结构应该是Y字型的。
也就是说,c1之后的节点都是交叉节点。下面的问题就是如何确定c1这个节点,我们可以设两个指针分别遍历两个链表,然后对比节点的值,但是两个链表可能是不等长的,我们可以先让长度较大的链表指针先走|len(listA)−len(listB)|步,然后在同步进行。
时间复杂度O(n),空间复杂度O(1)。
c++代码如下:
//求出链表长度
int getListLength(ListNode *head)
{
int len = 0;
ListNode *tmp = head;
while(tmp) tmp = tmp->next,len++;
return len;
}
//判断交叉节点
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *a = headA, *b = headB;
int ab = getListLength(a)-getListLength(b);
if(ab > 0)
{
while(ab) a = a->next, ab--;
}
else if(ab < 0)
{
while(ab) b = b->next, ab++;
}
while(a && b)
{
if(a->val == b->val) return a;
a = a->next;
b = b->next;
}
return NULL;
}
5.大端、小端,32位与64位有没有区别
记住一句话:尾端放在低地址,就是小端。尾端放在高地址,就是大端。
1. 什么是大端,什么是小端:
所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
2.为什么会有大小端:
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
3.大小端在内存中的存放方式举例:
例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 0x4000 0x4001
存放内容 0x34 0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址 0x4000 0x4001
存放内容 0x12 0x34
32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x78 0x56 0x34 0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x12 0x34 0x56 0x78
4.如何测试编译器是大端还是小端:
下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:
#include<stdio.h>
int main()
{
short int x;
char x0,x1;
x=0x1122;
x0=((char *)&x)[0]; //低地址单元
x1=((char *)&x)[1]; //高地址单元
printf("x0=0x%x,x1=0x%x",x0,x1);// 若x0=0x11,则是大端; 若x0=0x22,则是小端......
return 0;
}
6.http报文格式
(1)http请求报文:
请求行:方法字段、URL字段、HTTP版本号
首部行:Host、Connection、User-agent、Accept-language
空行
实体主体
(2)http响应报文
状态行:版本、状态码、短语
首部行:Connection、Date、Server、Last-Modified、Content-Length、Content-Type
空行
实体主体
7.C++实现单例模式
8.在一个存有大量字符串的文件中剔除重复字符串
CVTE一面(处女现场面)
1.判断有多少个类实例化对象。判断基类和派生类分别有多少个实例化对象。
2.写一个返回绝对值的宏。(忘了在最外层加个大括号)
3.写一个接口,判断字符串是否为回文。(忘了对该字符串的长度是否为0做判断)
4.include和extern的区别(当时以为答得不错,回来上网搜索了下才发现答得那么渣。面试官还一直“嗯好嗯好”来附和。看来面试官的反应也是难以捉摸的)。然后问extern fun(int a); 但是使用则是fun(a,b),问出错不。
5.vector和数组的区别
6.位域(直接说没听说过)
7.交叉链表
8.洗牌算法(orz,直接问我博客里写的,然而这个算法记得很模糊。)
CVTE二面(处女远程面)
为了进行CVTE这个二面,可是体验了一把过山车。2017.3.28去酒店进行了一面后,被告知当晚可以通过微信公众号进行查结果。当晚8点过后我就去公众号查结果了,可是显示的却是之前笔试的结果,一直显示新一轮结果还没出来。9点、10点、11点、0点,每隔一会我就查一下结果,然而显示的结果一直没有变化。心灰意冷,以为自己看来是一面挂了。本来想第二天其他人去二面的时候自己也跑去酒店问问,但还是放弃了。
就这样,来到了4.1号愚人节,当天CVTE公众号发了个关于比赛的推送,我也就习惯性地点了下查询结果。纳尼!我的天!这是闹哪样!结果竟然显示我一面过了,一面过了,面过了,过了,了。。。我赶紧抱着尝试的心态向后台发了信息,过了一会他也回了,让我发姓名和邮箱,他去查查数据。
过了两天,后台发信息给我说我确实一面过了,说是会在近期安排远程二面。
好吧,果然一面自己被漏掉了,苍天啊,我怎么那么背。
到了今天,4.5号,约了晚上7点的远程面试,是通过牛客网进行的。期间和面试官聊了自己做的项目,然后问了我网络方面的知识,我都答得还不错(嗯,自我感觉)。接着就让我做道题,题目是求一个整数数组中出现次数第k多的整数。讲了下思路,就是用map进行存储整数和出现的次数,再对map按value进行排序。代码实现起来的时候因为之前没有经历过实现按值排序,所以卡住了。面试官说没事,思路还是正确的。然后就问了我有没有什么想问的。我问了2个问题后就结束了二面了。
接着,重点来了。刚结束二面5秒,对,5秒!公众号就能查到结果了。嗯,如预想的那样,二面挂了。
再次心灰意冷。。。
百度一面:
1.sizeof
2.大端小端。intel处理器的大小端,网络序
3.malloc和new的区别
只答出了分别对应free和delete。(面试官说更重要的是new还会调用构造函数)
4.c++里存ip要用的数据类型最好是?
答了string。orz,好像一个int就可以了。刚好32位。
5.指针的偏移
6.讲一下atoi函数的实现思路
注意char*指针是否为空、正负、溢出
7.100万个数据里找出前100大的数据
堆排序。构造元素个数为100的最小堆。
8.50个红球和50个篮球,放入两个箱子,怎么样放置才能使拿到红球的概率最大
一个箱子放1个红球 另一个放49红球和50篮球 拿到红球概率=0.5+49/99约等于0.75。
9.4次挥手
10.ps命令,查看内存载荷的命令。
答用top。(还有vmstat)
11.gdb命令:n,b,bt
当时忘了bt是干啥的。(打印当前的函数调用栈的所有信息。)
腾讯一面2017.4.13:
1.进程空间分布
从低地址到高地址,分别是:只读的代码和数据、已初始化的全局变量和静态变量、未初始化的全局变量和静态变量、堆、共享存储区的映射、栈、命令行参数和环境变量。
2.成员变量存在哪
对象的成员变量就放在对象内
如果对象本身是堆栈(SS)里的,那么它的(非静态)数据成员也在堆栈里
如果对象本身是堆里的,那么它的(非静态)数据成员也在堆里
如果对象本身是DS里的,那么它的数据成员也在DS里
3.编译器为一个空类实现了什么函数。
4.多态。构造函数可以是虚函数吗。
5.vector的size和capacity
6.模板的实现机制
当函数模板被调用时,编译器对函数实参类型的检查决定了模板实参的类型和值的这个过程叫做模板实参推演。
然后编译器用推断出的模板参数来实例化一个特定版本的函数或者类。
当编译器遇到一个模板定义时,它并不生成代码。只有当我们实例化出模板的一个特定版本时,编译器才会生成代码。
为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,因此,与非模板代码不同,模板的头文件通常既包括声明也包括定义。
当两个或多个独立编译的源文件使用了相同的模板,并提供了相同的模板参数时,每个文件中就都会有该模板的一个实例。这样额外开销会非常严重。可以通过显示实例化来避免。也就是用extern。比如:
extern template class A; //声明
template int compare(const int&,const int&); //定义
一个模板特例化就是一个用户提供的模板实例,它将一个或多个模板参数绑定到特定类型或值上,当我们不能(或不希望)将模板定义用于某些特定类型时,特例化非常有用。使用如下:
template <>
int compare(const char*p1,cosnt char*p2) {}
7.堆和栈的区别
8.物理地址逻辑地址
9.进程间通信方式。文件是其中一种吗。
文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。
Win32 API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。
应用程序有三种方法来使多个进程共享一个文件映射对象。
(1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄。
(2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。
(3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。
文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。
10.子进程从父进程继承了什么
子进程继承父进程的属性:
实际用户ID,实际组ID,有效用户ID,有效组ID;
附加组ID;
进程组ID;
会话ID;
控制终端;
设置用户ID标志和设置组ID标志;
当前工作目录;
根目录;
文件模式创建屏蔽字(umask);
信号屏蔽和安排;
针对任一打开文件描述符的在执行时关闭(close-on-exec)标志;
环境;
连接的共享存储段;
存储映射;
资源限制;
父子进程之间的区别:
fork的返回值(=0子进程);
进程ID不同;
两个进程具有不同的父进程ID;
子进程的tms_utime,tms_stime,tms_cutime以及tms_ustime均被设置为0;
不继承父进程设置的文件锁;
子进程的未处理闹钟被清除;
子进程的未处理信号集设置为空集;
子线程继承主线程的属性:
进程中的所有信息对该进程的所有线程都是共享的;
可执行的程序文本;
程序的全局内存;
堆内存;
栈;
文件描述符;
信号的处理是进程中所有线程共享的(注意:如果信号的默认处理是终止该进程那么即是把信号传给某个线程也一样会将进程杀掉);
子线程特有的:
线程ID;
一组寄存器值;
栈;
调度优先级和策略;
信号屏蔽字;
errno变量;
线程私有数据;
注意:由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程数据段、栈和堆的完全副本。作为替代,使用了写时复制技术。这些区域由父进程和子进程共享,而且内核将它们的访问权限改变为只读。如果父进程和子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本 。
11.三次握手
12.服务器tcp调用函数
13.堆排序的时间复杂度空间复杂度,快速排序的时间复杂度空间复杂度,什么时候最坏情况
14.大堆小堆,怎么实现。可以用链表吗
15.10分钟内写快速排序
华为2017.4.14:
两轮面试,都没有问技术问题,没错,一个问题都没问!一面问了项目和获奖情况,二面综面问了成绩、项目、家庭情况。然后,没了。等通知。嗯。
腾讯二面+hr面2017.4.15:
4.13参加完鹅厂的一面后,自我感觉发挥不错,心想应该能过。当天晚上一直查状态,都没看到更新。心想看来还是挂了。
4.14从一大早就去华为面试,到12点多才坐车回来。回到学校看手机,发现鹅厂一面竟然过了!!!!当天赶紧继续准备第二天的二面。半夜两点实在撑不下,先睡了。15号早上7点起床继续准备。
因为预约时间是11:50,所以在10点半就坐公交出发了。虽然因为车上的电子显示屏误报信息坐过了一站,但还是有惊无险提前到了酒店。
刚签到坐下不久,就被通知可以去房间面试了。
一进去就让我直接手写代码,实现双向链表删除指定节点。
接着是讲讲思路。
然后是让我自己挑一个项目说说。
说完后面试官继续问技术问题。
1.new和malloc的区别(其实这个在百度一面的时候被问到过了):我答了两个,一个是new对应的释放操作是delete,malloc对应的释放操作是free。第二个区别是new的时候还会调用构造函数。说完之后面试官问还有其他区别吗?我就说暂时就想到这两个了。
2.给了我一道题,是结合union和struct的求sizeof题,分别是4字节对齐、2字节对齐、1字节对齐的情况下。我算了一下就写了答案,他看了下说好,然后也没有说什么了。
3.接着让我画出tcp的三次握手并解释下过程。然后问我这其中传输了什么数据。我画完并解释过程后,说了下传输的数据就是报文段的结构中的数据,然后顺势说了下tcp报文段的结构。源端口号、目的端口号、序号、确认号、报头长度、标志、窗口大小、检验和、紧急指针。也不知道这样答面试官的问题对不对。orz。
4.接着问我一个经典的走台阶问题,就是一次只能走一步或者走两步,走n阶的话有多少种走法。
在纸上画了画后就跟面试官解释过程了。
然后,然后就没有然后了!面试官说好了,今天就面到这了。(我的天!就这4道题和项目介绍?)和面试官握了握手,表示感谢后并道别就走了。
出来的路上一直在想,面试时间那么短,看来面试官对我不感兴趣啊。没戏了。
因为累,所以回大厅倒了杯水坐下来休息。心想看看状态改了没,一看,吓一大跳!状态显示hr面试!!!!我竟然过了!!!又过了几分钟,收到面试短信了,下午3:30。所以我索性也没回学校了。吃完饭就在酒店大厅歇息。
2点30我就上去签到等待了,不过还是到了3:40才轮到我。ok,接下来就是和hr姐姐愉快地交谈啦。
hr姐姐确实好容易相处,全程微笑着看着我,当个倾听者。我先是进行了自我介绍,因为我在介绍中有说我担任过老乡会会长,所以hr姐姐就顺势问我怎么组织活动,安排任务。然后问了个自己犯了个错误被同事举报导致自己被领导骂我会怎么处理的问题。对鹅厂有什么认识吗?之后问我有什么想要了解的吗。还跟我讲了好多鹅厂的信息,比如企业文化啦。
最后和hr姐姐愉快地握了个手就道别啦。不知道最后能不能顺利进入鹅厂,等待中。。。
2017.4.22:
今天收到了华为的短信,说是通过了面试,进入签约资源池。不知道这意思是过了还是说是备胎呢?
2017.4.24:
等了一个多星期后,终于在今天下午5点前接到了腾讯的电话!全身心投入奋斗了一个多月终于成功了!感恩所有!
接下来就继续学习,做技术积累了。在鹅厂实习要好好努力了,目标:秋招转正!
“未来的自己会感谢现在拼搏进取的你。”