初级程序员面试不靠谱指南(一)
“来到这英雄宴中的人物,就算本身武功不是甚高,见识也必广博,“太祖拳法”的精要所在,可说无人不知。乔峰一招打出,人人都是情不自禁的喝了一声采!这满堂大采之后,随即有许多人觉得不妥,这声喝采,是赞誉各人欲杀之而甘心的胡虏大敌,如何可以长敌人志气,灭自己威风?但采声已然出口,再也缩不回来,眼见乔峰第二招“河朔立威”一般的精极妙极,比之他第一招,实难分辨到底那一招更为佳妙,大厅上仍有不少人大声喝采。只是有些人憬然惊觉,自知收敛,采声便不及第一招时那么响亮,但许多“哦,哦!”“呵,呵!”的低声赞叹,钦服之忱,未必不及那大声叫好。乔峰初时和各人狠打恶斗,群雄专顾御敌,只是惧怕他的凶悍厉害,这时暂且置身事外,方始领悟到他武功中的精妙绝伦之处。但见乔峰和玄难只拆得七八招,高下已判。他二人所使的拳招,都是一般的平平无奇,但乔峰每一招都是慢了一步,任由玄难先发。玄难一出招,乔峰跟着递招,也不知是由于他年轻力壮,还是行动加倍的迅捷,每一招都是后发先至。这“太祖长拳”本身拳招只有六十四招,但每一招都是相互克制,乔峰看准了对方的拳招,然后出一招恰好克制的拳法,玄难焉得不败?” ---------《天龙八部》
这是天龙八部中乔峰在聚贤庄使用最基本最简单的太祖长拳大败各路群雄的描写,我每每读到此处颇有感触,其实想变成大牛,除了能够学习上乘的武功,更要修炼自身本身的内功,内功强大了,任何技能均可信手拈来,无坚不摧。
起这个题目的原因真心不是为了黑《程序员面试宝典》,而是我觉得我的水平只能是不靠谱哪个类型的。上面这一大段废话的原因是因为最近经常被人问及去笔试面试的时候都会考些什么,其中有大部分人都拿着一本《程序员面试宝典》来寻找答案,按照我目前的水平,我还没有资格评价一本书的质量,但是我从自身亲身感受过的各种笔试面试出发,这样一本书虽然可能能够蒙骗住面试官的头两个问题,但是最终还是会暴露自己本身根本就没有练好的那一部分。每次别人问我看什么书来应对面试的时候,我总有一种说不出来的感觉,首先“应对”这个词总感觉怪怪的,不过也难怪,我们从小学开始就是“应对”各种考试,而不是怀着一种考试只是为了检验你学习的一种手段而已,学习好了,自然也考的好。不过,话说回来,在目前这样一个做什么要有个明确目的,讲究速成的文化中,这样的想法也很正常。面对这个问题,我都是用类似“比如你要准备C/C++语言你可以先看看《essential C++》,《The C programming language》这类的并不是很厚但是确实能够涵盖绝大部分知识的书,然后有时间再去看看《c++ premier》,《effective C++》这些的。"我觉得这些书本身写的就很吸引人,看起来也很快,可以指引着你思考,我觉得比看《宝典》这样的书更加心安,同时也是对内功的一次修炼。
虽然说实习加上工作已经快有一年了,对于写程序有了新的感触,虽然说你笔试面试准备的,被问到的大部分的内容在工作中不会经常用到,但是对于写程序确实是一个思路的拓展,它可以给你提供一个解决问题的大门,指引着你不断的提高自己的程序水平,这就像内功和武功一样,只有积累的更深的内功,你才能在实际工作中得心印手,哪怕使用最简单的编程方法,你程序的效率和可读性也比别人强。
去年笔试、面试的时候准备过一些东西,最近翻出来看看加上已经遇到很多下一届的问我如何面对面试,最近也闲来无事,加以整理,希望能够给需要的人有所帮助,我努力让我的这些信笔涂鸦可以涉及到深层一点部分,这样不会走《宝典》那种的那条其实我觉得不怎么适合的路,如果有错误的地方,欢迎指正,。
想想我笔试、面试的时候,所遇到的题目大约涉及到如下几个方面,C\C++\C#语言本身(我没有去参加过关于Java的面试,所以这一方面我确实不知道),数据结构,算法,多线程编程,网络编程,数据库,linux,设计模式。所以,当时我大致准备的也是这几个方面的内容,那么,我先从语言本身开始吧。
一、扑朔迷离的const
先从最基本简单的开始吧,const在C/C++里面作为一个声明常量的关键词,可以防止程序员在写程序的过程中不小心对这些值进行了修改。虽然在C++中有时候会用#define声明一个常量,但是这二者还是有略微的不同,比如如果用const声明的常量会进行类型检查,而#define的只是简单的替换。关于const的用法和注意的地方有很多很多,能全部弄清楚我觉得真的很牛了,我只能凭我的能力尽量列举出我已知的。
1.最常见到的一种可能被考的形式,int *const和int const *有什么区别,从我个人来讲,我最初很难记住这两个的差别,看了各种关于这方面的书都是当时能记得住,想的明白,但是过一段时间就又忘了,后来仔细思考了一下原因,我觉得是我们没有掌握着背后的原理,倘若我能够像高中推数学公式那样推出来,我才能不再被这个问题所苦恼。
int i=0,j=1; int const ci=0; int *pointer=&i; int *const ic_pointer=&i;//指向的值可以被修改,不能修改指向的东西(地址) int const *ci_pointer=&i;//指向的东西(地址)可以被修改,值不可以被修改
从上面的代码开始分析,我们分别声明了这两种指针,注释说明了这两种指针的不同,也正如很多书中写的那样。通俗一点,可以使用*ic_pointer=1修改其指向的值,可以使用*ci_pointer=&j,修改其指向的变量,反之,则会出现编译错误。为什么会出现这样的情况呢?从最基本的开始,我们想象这些指针,变量啥的都是存在于一个连续的内存空间之中,假设如下:
再强调一遍,这只是假设,事实上,你也可以在程序中把这些内容变量的地址全部输出出来,自己看看。从左到右编上序号0xE1-0xE6(你可以用任何你喜欢的,我用E是因为我觉得这个字母看的比较顺眼),用这个形式是为了防止后面发生混淆,那么E1这个格子里面的数值应该是0,E2的j是1,E3的ci是0,那么pointer这个格子里面的数值应该是什么呢?它所占内存里面所包含的应该是它所指向的内容的地址,也就是我们这里格子的编号,也就是E1,E4的pointer格子里面的值是E1(不是0,而就是E1这个值),同理后面两个格子也都是E1这个值。如下图所示:
从格子E3开始,E3是个const的值,也就是说这里面的这个0是不可以更改的,这很好理解,接下来,我们查看E4格子的值,从声明看,这是一个指针,指向E1格子里面的值,E1格子里面的值是个整数型,如果把这个值改成E2,那么其指向的就是j这变量,这也很好理解。接下来进入正题,对于int *const ic_pointer这个声明,可以看到这个是一个const的指针(从*的位置,int *pointer就是一个指针,指向int型的内容),指向一个int的内容,既然const类型是不能改变该内存所存储的值,那么const指针也是不能改变该内存所存储的值,也就是我们不能把E1改成E2,也就是说其指向的地址不能被改变,但是指向的值能不能改变呢?这个和这个指针没有关系,所以,你可以通过该指针改变其指向的值。同理,让我们再来思维训练看看int const *ci_pointer,先来翻译一下,这是一个指针,指向的内容是int const,或者const int,无所谓,注意,这里面没有规定这个指针是const,只规定了其指向的值是const,虽然它不知道它指向的值是不是const,但是从它来看,它指向的值是不能更改的,也就是你不能通过我来改我指向的值,但是我格子本身的值可以随便改,比如将E1改成E2,这也就是说可以修改其指向。
上面看了之后,可能还是有点混乱,稍微总结一下,const的关键字出现的位置决定了是哪个部分不能改变,如果出现在*之后,那么其修饰的是这个指针,如果在之前,那么其修饰的前面出现的哪个关键字。还要注意的一点,const只能管自己修饰的东西,被人修饰的它无权管辖,比如ci_pointer所指向的值i,并不是一个声明为const的值,所以就算你指向的是i,它还是可以通过i=1之类修改自己格子的值,但是你不能通过*ci_pointer修改值,因为从声明上看它认为自己是指向const int的,所以它所接到的命令是我不改掉我指向的值。
从上面介绍的内容,如果你能想明白ci_pointer和ic_pointer哪个能指向ci,你就能想明白整个过程,也就不需要强行记忆了,虽然这可能会多花30秒,但绝对有底气的说不会错。关于指针的内容就更多了,在后面我还会再进行详细阐述的。
另外,关于const这个关键字的准确理解方式应该是这样的:const关键字是告诉编译器该变量不能作为一个左值出现在任何表达式中,也就是将其设置为只读的(read-only)。我切身经历让我深深的体会到当你能完全理解这句话的时候,才是对const真正的理解了。
2.const在C和C++中的不同。const虽然在这两种语言都有,但是,在这两种语言中有什么不同这个问题不知道有多少人考虑过,但是我曾经也被问到过这个问题,在绝大多数情况下,确实很难发现其不同点,但是如果将程序分布到几个不同的文件中,并且使用了const,立马就能明白这两个的不同。
假设我在file1.c中定义了一个全局变量const int i = 2;如果我想在另外一个文件file2.c使用这个值,你需额要使用extern const int i;但是如果是在C++中,你想做同样的事情,你必须在fiel1.cpp中使用如下语句extern const int i=2;为什么会这样呢?因为const标示的常量在C语言中具有的默认连接性质是external的,而在C++中是internal,这又有什么关系呢?如果下面的解释不能让你清楚的明白,那么,请先记住一点好了:在C语言中,一个const value在全部工程(程序)范围内都是可见的,而C++中const value只能在定义的这个文件中是可见的。要弄明白这个问题,首先要能够明白连接性质(linkage)到底是什么概念,又会影响到什么,关于这部分内容,我最推荐的书无疑是《c与指针》,我觉得那里面讲的很详细,在这里,为了解释这个问题,我只选择internal和external来简要说明一下。
当你写好你的一个实现文件之后,开始编译,你的编译器会根据你的程序生成translation unit(TU)这样一个东西,先别管这事啥玩意儿,反正这是一个包含了你所有程序加上#include的那些.h的头文件里面的内容,你的整个的程序是由很多个这样的TU组成的。而internal linkage只的是所有的东西只是在这个TU中是可见的,而external linkage表示的是所有这些东西都不仅仅存在一个TU之中,也就是,可以在整个程序都是可见的。所以,在C中,在某一个文件中定义了一个const value,你可以轻松的在另外一个文件中使用它,只要使用extern关键字。但是在C++中,如果同样这样定义出一个const变量,那么在另一个文件中声明的const变量就是一个新的变量,因为在另一个文件(另一个TU)中并不存在这个变量,好在可以使用extern可以改变其连接属性,这样,就可以和在C语言中一样使用,定义该变量,另外一个可以神奇的常用的改变连接属性的关键字是static,这些展开已经大大超出了本身的内容,同样在后面也会有详细说明的。
突然发现前面的废话太多了,显得有点长,考虑到关注兴趣和查找速度的和文章长度成反比的黄金定律,剩余部分我换到稍后的下一篇中。我会将这本不靠谱指南写下去的,争取在9、10月校园招聘期之前写完大部分,虽然能力不是很高,但是我真心想给一起需要的将要经历我曾经经历的人一些帮助,最后,再强调一遍,我真心不是要黑《程序员面试宝典》。