版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、有关指针的经典面试题 C语言为何如此长寿并实用?C+为什么有那么多精彩?指针可以说是C/C+中的灵魂所在,虽然早期中pascal也有指针,但是和C/C+比起来不是一个级别的.今天为大家深入浅出的解析一下指针的有关笔试,面试题.所有题目来源网络,分析是我写的.0.预备知识,最基础的指针其实最基础的指针也就应该如下面代码:int a;int* p=&a;也就是说,声明了一个int变量a,然后声明一个int 的指针,*p指向a的地址,&也就是取地址符号,而*是指针中取内容的符号,仅仅在声明的时候标记这个变量是指针.可能有点绕口,但是看代码来的容易的多.2.忽悠人的陷阱,str和*str的区别先告诉你
2、哦,下面的题目可是陷阱啊.说说程序结果.char str1 = “abc”;char str2 = “abc”;const char str3 = “abc”;const char str4 = “abc”;const char *str5 = “abc”;const char *str6 = “abc”;char *str7 = “abc”;char *str8 = “abc”;cout ( str1 = str2 ) endl;cout ( str3 = str4 ) endl;cout ( str5 = str6 ) endl;cout ( str7 = str8 ) endl;解答:s
3、tr1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。怎么样?都输出true?那显然你中标了.而且cout输出bool值的时候,就算全是真也应该都输出1啊.4个1?那也不对.答案是0011,不信你试试.为什么呢?其实都说了这题是个大陷阱,因为这题根本不是比较字符串内容!而是比较字符串的地址.哦.恍然大悟.那为什么前两个是假呢?因为这可是说是一个深拷贝/浅拷贝的问题.当字符串是数组形式声明并初始化,编译器认为是新数组,分配新空间,但不是深拷贝,因为根本就不算拷贝.而如果是相同的字符串,用指针声明,那就是比较如
4、果有一样的字符串,就直接把新指针指过去,这是正宗的浅拷贝.哇哈.你就中计了.3.str用sizeof判断会出错么?应该说我们常常用指针有很多时候是解决字符串的问题,一般我们用strlen,这当然没有问题,但是要你编一个呢?看看下面这个MyStrlen有问题么?int MyStrlen(char str) return (int)(sizeof(str)-1);呵呵.咱们上当过一次.这个当然也是不对的.不错.这个函数是错的.为什么呢?首先,可以告诉你,无论何时,返回的总是3.额.是不是很奇怪,为什么不是数组长度呢?str不是char数组指针么?不错.确实是数组的指针,但是,当用函数传递的数组指针
5、的时候就自动退化为指针了,而指针的长度是4,你减去1了自然就是3了.但是如果按照下面代码就可以得到正常的值.char str=hello world;int len=sizeof(str)-1; /记得减1哦,最后有0结尾coutlen;这样输出的是正常值,也就是你所希望的11;4.注意数组指针和指针继续上面的话题,刚刚提到了数组指针和指针,现在看看下面这端程序代码:int a5=1,2,3,4,5;int *ptr=(int *)(&a+1);cout*(a+1)*(ptr-1);呵呵.BaihowFF总是给陷阱.肯定不是想当然的说就是21.确实.答案是25.额.奇怪吧.为什么呢?首先,a是
6、一个数组,所以编译器解释&a就是a的全部长度,就是说(&a+1)也就是说移动了一个数组,指向了并不存在的a5,所以ptr-1才会指向a数组的最后一个元素a4,而a+1和a1是一样的.所以答案是25,如果你去掉了(&a+1)的括号,那么答案就是想当然的21了.呵呵.很微妙吧.5.注意指针要分配给足够的空间新手在刚刚接触指针的时候经常会忘记给指针分配空间,直接用肯定是有问题的,那么下面的程序呢?char a;char *str=&a;strcpy(str,”hello”);coutstr;BaihowFF是坏蛋.总会下套.呵呵.确实是圈套.这段程序能够输出hello,但是输出后就崩溃了.原因就在你
7、分配str指针的时候仅仅给了1字节的空间,但是你拷贝了6字节过去(不要忘记了最后的0结束).运行输出后程序因为访问了没有分配的呵呵空间,当然崩溃了.如果你只strcpy(str,);那程序是可以正常运行的.6.小心编译器的指针字符串初始化经常我们想自己处理字符串,但是像下面的初始化是很危险的!char* s=AAA;coutsendl;s0=B;coutsendl;你可以拿这段程序去编译.没错!编译器报告正常!.这是最要命的.其实程序不能运行的.输出AAA后就崩溃了.为什么?因为当你在第一句初始化的时候,编译器就认为这是个字符串常量了.再做数组操作的时候肯定错了罗.最好的习惯是声明一个指针,用
8、new分配空间,然后用库函数操作,比如strcpy,strcat等等.7.让人一眼看上去迷糊的函数指针看看这句代表什么意思?int (*s10)(int);咦.这是什么?其实这是一个函数指针数组,指向了一组int fun(int)的函数,第一眼确实让人有点迷糊.但是请习惯这样.8.注意函数传递指针的时候是副本副本?又下副本?.汗.老兄.不是这个意思.别沉浸在WOW里了啊.看看下面程序的问题:void GetMemory(char *p) p=new char100;strcpy(p,hello world);void main(void)char *str=NULL;GetMemory(str
9、);coutstr;delete str;str=NULL;当然了.喜欢下套的BaihowFF又给了错程序.错在哪呢?看上去是对的,而且编译器编译也正确啊.怎么就是不能通过呢?而且还崩溃了.好费解吧.其实原因很简单.GetMemory这个函数出问题了!函数参数是不能传递分配空间的.因为传递过去实际上是一个副本p.不能返回的.而且你在delete那就是件很危险的事情.因为压根没有内容.那我实在想这样用函数分配怎么办呢?像下面这样改一下就ok了:void GetMemory(char *p) / 改成晦涩难懂的指针的指针 *p=new char100; /给*p的分配地址strcpy(*p,hel
10、lo world); / 拷贝内容到*pvoid main(void)char *str=NULL;GetMemory(&str); /这地方取地址coutstr;delete str;str=NULL;这样就能正常工作了,但是看起来好别扭啊.嗯.确实.但是还可以用其他方法哦.你想想.肯定有办法的.9.请时刻记住要初始化字符串嗯.这点大家都知道.那你猜猜下面的程序结果是多少?char a10;coutstrlen(a)endl;答案应该让你以外.竟然是15.没道理吧?!其实strlen函数的结果和是否初始化有关的.虽然你分配了空间.但是没有初始化.库函数会出错的.sizeof不受影响.切忌初始
11、化哦.10.小括号,大区别看看这两端声明,有什么不同?我直接在注释里告诉你答案吧.这样好看点.char (*str)20; /str是一个数组指针,即指向数组的指针,C语言里专用来指向二维数组char *str20; /str是一个指针数组,其元素为指针型数据千万别小看括号哦.区别大了吧.一些笔试题目:预处理器(Preprocessor)1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL我在这想看到几件事情:1) #define 语法的基本知识(例如:不能以分号
12、结束,括号的使用,等等)2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。4) 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。2 . 写一个标准宏MIN ,这个宏输入两个参数并返回较小的一个。 #define MIN(A,B) (A)=(B)?(A):(B)这个测试是为下面的目的而设的:1) 标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(in
13、line)操作符 变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。3) 懂得在宏中小心地把参数用括号括起来4) 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事? least = MIN(*p+, b);3. 预处理器标识#error的目的是什么?如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录
14、去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。死循环(Infinite loops)4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?这个问题用几个解决方案。我首选的方案是:while(1)一些程序员更喜欢如下方案:for(;)这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:我被教着这样做,但从没有想到过为什么。这会给我留下一个坏印象。第三个方案是用 gotoLoop:.goto Loop;应试者如给出上面的方案,这说明或
15、者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。数据声明(Data declarations) 5. 用变量a给出下面的定义 (笔试到一次)a) 一个整型数(An integer) b)一个指向整型数的指针( A pointer to an integer) c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r d)一个有10个整型数的数组( An array of 10 integers) e) 一个有10个指针的数组,该指针是指向一个整型数的。(An arra
16、y of 10 pointers to integers) f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers) g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take a
17、n integer argument and return an integer )答案是: a) int a; / An integer b) int *a; / A pointer to an integer c) int *a; / A pointer to a pointer to an integer d) int a10; / An array of 10 integers e) int *a10; / An array of 10 pointers to integers f) int (*a)10; / A pointer to an array of 10 integers
18、g) int (*a)(int); / A pointer to a function a that takes an integer argument and returns an integer h) int (*a10)(int); / An array of 10 pointers to functions that take an integer argument and return an integer 人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或
19、者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?Static 6. 关键字static的作用是什么?这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。3) 在模块内,一个被声明为静态的函数只可被这一模块内
20、的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。4. static有什么用途?(请至少说明两种)1.限制变量的作用域2.设置变量的存储域Const 1.与const在一起的时候常常声明的时候会让人一头雾水,比如下面的声明,均是声明一个char* p的指针:char * const p; /修饰指针p, p不可变,p指向的对象可变char const * p; /修饰指向的对象,p可变,p指向的对象不可变cha
21、r const * const p; / 内容和指针都不能改const char * const p; / 同上.内容和指针不能改 char const和const char是一回事,“颠倒写”都是可以的额.别晕,别晕.其实方法很简单.你别真死记硬背.其实可以以*为分界符,在*左边有const就说明内容不能改,在*右边就说明指针不能改,而左边的char和const顺序是不要紧的.呵呵.你也可以理解成const是修饰后面的,正常顺序应该这样:const char * const p; 是不是看起来简单了?7关键字const有什么含意?我只要一听到被面试者说:const意味着常数,我就知道我正在和
22、一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着只读就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?const int a;int const a;const int *a;int * const a;int const
23、* a const;/*/前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:1) 关键字const的作用是
24、为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。Volatile 8. 关键字volatile有什么含意?并给出三个不同的例子。volatile的作用是: 作为指令 HYPERLINK /vi
25、ew/390935.htm t _blank 关键字,确保本条指令不会因 HYPERLINK /view/487018.htm t _blank 编译器的优化而省略,且要求每次直接读值.一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:1) 并行设备的硬件寄存器(如:状态寄存器)2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3) 多线程应用中被几个
26、任务共享的变量回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。1)一个参数既可以是const还可以是volatile吗?解释为什么。2); 一个指针可以是volatile 吗?解释为什么。3); 下面的函数有什么错误:int square(volatile int *ptr) retur
27、n *ptr * *ptr;下面是答案:1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。2); 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修该一个指向一个buffer的指针时。3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:int square(volatile int *ptr) int a,b; a = *ptr; b = *ptr; return a * b;由于*ptr的值可能被意想不到地该变,
28、因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:long square(volatile int *ptr) int a; a = *ptr; return a * a;Typedef 15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:#define dPS struct s *typedef struct s * tPS;以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么?这是一个非常微妙的问题,任何人答对这个问题(正当的
29、原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子:dPS p1,p2;tPS p3,p4;第一个扩展为struct s * p1, p2;.上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。位操作(Bit manipulation) 9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。对这个问题有三种基本的反应1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。2) 用bit
30、fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。3) 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:#define BIT3 (0 x1 访问固定的内存位置(Accessing fixed memory loc
31、ations) a|=0 x04; a&=0 x04a|=(1 3); a&=(11) 求x与y的平均值。该方法效率不高但是不会产生高位溢出。x&y求出相同的位,只保留一份相同位,也就是求了相同位的均值xy求出不同的位之和,再右移一位(除以2),求出了不同位的均值。(注意优先级高于)数组中数字两两相同,有两个不同,找出这两个分析:这是一道很新颖的关于位运算的面试题。首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个
32、数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现依次的数字,因为那些出现两次的数字全部在异或中抵消掉了。有了上面简单问题的解决方案之后,我们回到原始的问题。如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其他数字都出现两次。如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0,也就是说在这
33、个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。因此到此为止,所有的问题我们都已经解决。基于上述思路,我们不难写出如下代码:int findNotDouble(int a, int n)int result = a0;int i;for(i = 1; i n; +i)result = ai;return res
34、ult;void findOutTwoOdds(int a, int n, int &odd1, int &odd2)int odd = findNotDouble(a, n); int y=1, i;/ find y to indicate the first bit which is 1 in (ab)for(y = 1; y = odd; y = 1)if(y & odd)break;/ divide the datas into two groupsfor(i = 0; i 6) ? puts( 6) : puts(6);当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无
35、符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。13. 评价下面的代码片断:unsigned int zero = 0;unsigned int compzero = 0 xFFFF; /*1s complement of zero */对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:unsigned int compzero = 0;这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非
36、常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧.引用与指针1) 引用必须被初始化,指针不必。2) 引用初始化以后不能被改变,指针可以改变所指的对象。3) 不存在指向空值的引用,但是存在指向空值的指针。零值指针和NULL指针零值指针,被赋值为0的指针,是
37、值为0的指针。可以是任何一种指针类型,可以是通用变体类型void*也可以是char*,int*等等。空指针,其实空指针只是一种编程概念,就如一个容器可能有空和非空两种基本状态,而在非空时可能里面存储了一个数值是0,因此空指针是人为认为的指针不提供任何地址讯息。windef.h中NULL的定义:#ifndef NULL#ifdef _cplusplus /这个是指示是用C+来编译程序#define NULL 0#else#define NULL (void *)0)#endif#endifNULL 与 nullc语言和c+对大小写是敏感的,也就是说null和NULL是区别对待的。NULL代表空地
38、址,null只是一个符号。C+中式这样定义NULL的#define NULL 0但是没有定义null。 可以自定义为 #define null 0。此时null为符号TRUE与true ,BOOL 与bool1、类型不同BOOL为int型 typedef int BOOL;bool是C+中的类型,为布尔型 2、长度不同bool只有1个字节BOOL长度视实际环境来定,一般可认为是4个字节3、取值不同bool取值false和true,是假和真的区别BOOL取值FALSE和TRUE,是0和1的区别空类指针classApublic:staticvoidTest1()coutm_iStaticendl;
39、 voidTest2()coutTest2endl;voidTest3(intiTest) coutiTestendl; voidTest4()coutm_iTestendl;virtual void Test5()cout “Test5” Test1(); /call1p-Test2(); /call2p-Test3(10); /call3p-Test4(); /call4p-Test5(); /call5程序中,除了Test4和Test5外,都能正常运行。对于类成员函数而言,此类的所有对象共用这个成员函数体。 当程序被编译之后,此成员函数地址即已确定(非虚函数,无动态绑定),在编译阶段根据
40、p的指针类型就能得出Test()函数调用的地址。成员函数之所以能把属于此类的各个对象的数据区别开, 就是靠this指针。this作用域是在类内部,当在类的非静态成员函数中访问类 的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。 对于上面的例子来说,this的值也就是p的值。也就是说this的值为NULL。 Test1()是静态函数,编译器不会给它传递this指 针,所以call 1那行代码可以正确调用;Test2()和Test3()两个成员函数,虽然编译器会 给这两个函数传递this指针,但是它们并没
41、有通过this指针来访问类的成员变量,因此call 2和call 3两行代码可以正确调用; Test4()是成员函数要访问类的成员变量,因此要使用this指针,这个时候发现this指针的值为NULL,就会造成 程序的崩溃。Test5()为虚函数,需要动态绑定,即编译时不能确定该函数地址。只有在该类实例化时,往实例中添加一个指针来指向对应的虚函数来完成动态绑定后,才能确定地址,故Test5()不能正常访问。10. 什么是平衡二叉树?左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于111. 堆栈溢出一般是由什么原因导致的?没有回收垃圾资源12. 什么函数不能声明为虚函数?Construc
42、tor 构造函数13. 冒泡排序算法的时间复杂度是什么?O(n2)进程和线程线程是指进程内的一个执行单元,也是进程内的可调度实体.进程是资源分配和调度的最小单位,线程是CPU执行和调度的最小单位。与进程的区别:(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源. (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。网络编程中设计并发服务器,使用多进程
43、 与 多线程 ,请问有什么区别?1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。两者都可以提高程序的并发度,提高程序运行效率和响应时间。线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。2.测试方法 人工测试:个人复查、抽查和会审机器测试:黑盒测试和白盒测试思科1. 用宏定义写出swap(x,y)#define swap(x, y)x = x
44、 + y;y = x - y;x = x - y;2.数组aN,存放了1至N-1个数,其中某个数重复一次。写一个函数,找出被重复的数字.时间复杂度必须为o(N)函数原型:int do_dup(int a,int N)把数组里的数加起来减去1加到N-1的和(等差数列之和),就能得到重复的数int num;int s = 0;for(int I = 0;I N;i+) s += ai; num = s N * (1 + (N 1) / 2; / (num即那个重复的数) Sn=(a1+an)*n/2等差数列和等比数列求和公式等差数列求和:Sn=n*a1+n(n-1)d/2或Sn=n(a1+an)/
45、2 an=a1+(n-1)d等比数列求和:Sn=a1(1-qn)/(1-q) =(a1-an*q)/(1-q)3 一语句实现x是否为2的若干次幂的判断int i = 512;cout boolalpha (i & (i - 1) ? false : true) endl;4.unsigned int intvert(unsigned int x,int p,int n)实现对x的进行转换,p为起始转化位,n为需要转换的长度,假设起始点在右边.如x=0b0001 0001,p=4,n=3转换后x=0b0110 0001unsigned int intvert(unsigned int x,int
46、 p,int n)unsigned int _t = 0;unsigned int _a = 1;for(int i = 0; i n; +i)_t |= _a;_a = _a 1;_t = _t p;x = _t;return x;慧通:什么是预编译何时需要预编译:、总是使用不经常改动的大型代码体。 、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。12. 以下代码中的两个sizeof用法有问题吗?C易void UpperCase( char str ) / 将 str 中的小写字母转换成大写字母for( size_
47、t i=0; isizeof(str)/sizeof(str0); +i )if( a=stri & stri=z )stri -= (a-A );char str = aBcDe;cout str字符长度为: sizeof(str)/sizeof(str0) endl;UpperCase( str );cout str endl;答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeo
48、f作用于上只将其当指针看,一个指针为4个字节,因此返回4。一个32位的机器,该机器的指针是多少位指针是多少位只要看地址总线的位数就行了。80386以后的机子都是32的数据总线。所以指针的位数就是4个字节了。1.有以下表达式:int a=248,b=4;int const c=21;const int *d=&a;int *const e=&b;int const *f const =&a;请问下列表达式哪些会被编译器禁止?为什么?*c=32;d=&b;*d=43;e=34;e=&a;f=0 x321f;*c 这是个什么东东,禁止*d 说了是const, 禁止e = &a 说了是const 禁止
49、const *f const =&a; 禁止2.交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3;有两种解法, 一种用算术算法, 一种用(异或)a = a + b;b = a - b;a = a - b; ora = ab;/ 只能对int,char.b = ab;a = ab;ora = b = a;3.c和c+中的struct有什么不同?c和c+中struct的主要区别是c中的struct不可以含有成员函数,而c+中的struct可以。c+中struct和class的主要区别在于默认的存取权限不同,struct默认为public,而class默认为private
50、4.#include #include void getmemory(char *p)p=(char *) malloc(100);strcpy(p,hello world);int main( )char *str=NULL;getmemory(str);printf(%s/n,str);free(str);return 0;程序崩溃,getmemory中的实参赋值,p=str; p=(char *) malloc(100);后P的值改变,而str地址不变,所以 malloc 不能返回动态内存, free()对str操作很危险5.char szstr10;strcpy(szstr,01234
51、56789);产生什么结果?为什么?长度不一样,会造成非法的OS6.列举几种进程的同步机制,并比较其优缺点。原子操作 信号量机制自旋锁7.进程之间通信的途径共享存储系统消息传递系统管道:以文件系统为基础死锁死锁,是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面。1.进程死锁的原因资源竞争及进程推进顺序非法2.死锁的4个必要条件互斥、请求保持、不可剥夺、环路3.死锁的处理鸵鸟策略、预防策略、避免策略、检测与解除死锁Linux内核进程调度 操作系统中进程调度策略有哪几种?FCFS(先来先服务),优先级,时间片轮转,多级反馈内核分配时间片是有策略和倾向性的。换句话说,内核是偏心的,它喜欢的
52、是IO消耗型进程,因为这类进程如果不能及时响应,用户就会很不爽,所以它总会下意识的多分配CPU运行时间给这类进程。而CPU消耗进程内核就不太关心了。那么内核具体是怎么实现这种偏心呢?通过动态调整进程的优先级,以及分配不同长短的CPU时间片来实现。nice值是什么?其实就是优先级针对用户进程的另一种表示法,nice的取值范围是-20到+19,-20优先级最高,+19最低。当该进程刚被其父进程fork出来时,是平分其父进程的剩余时间片的。这个时间片执行完后,就会根据它的初始优先级来重新分配时间片,优先级为+19时最低,只分配最小时间片5ms,优先级为0时是100ms,优先级是-20时是最大时间片8
53、00ms。如果CPU用得多的进程,就把nice值调高点,等价于优先级调低点。CPU用得少的进程,认为它是交互性为主的进程,则会把nice值调低点,也就是优先级调高点。进程抢占:Linux系统是抢占式,始终运行优先级高的进程。8.类的静态成员和非静态成员有何区别?类的静态成员每个类只有一个,非静态成员每个对象一个9.纯虚函数如何定义?使用时应注意什么?virtual void f()=0;是接口,子类必须要实现10.数组和链表的区别数组:数据顺序存储,固定大小连表:数据可以随机存储,大小可动态改变网络通信12.ISO的七层模型是什么?tcp/udp是属于哪一层?tcp/udp有何优缺点?应用层
54、TELNET,FTP,HTTP 应用层表示层会话层运输层 TCP / UDP 传输层网络层 路由器 IP 互联网层物理链路层 交换机,网卡,网桥 网路接口层物理层 Hub,网线 (TCP/IP四层模型)TCP/UDP的区别TCP 服务提供了数据流传输、可靠性、有效流控制、全双工操作和多路复用技术等。与 TCP 不同, UDP 并不提供对 IP 协议的可靠机制、流控制以及错误恢复功能等。由于 UDP 比较简单, UDP 头包含很少的字节,比 TCP 负载消耗少。TCP: 提供可靠的、面向连接的、全双工的流传输服务,允许两个应用程序建立一个连接,并在任何一个方向上发送数据,然后终止连接。每个TCP
55、连接都被可靠的建立和友好的终止。 包头大小为20字节。应用:文件传输,电子邮件等UDP: 提供不可靠的,无连接的报文传输服务。每次向UDP请求发送一块数据,UDP会将数据放到一个单独的报文中来传输。UDP应用任意交互(一对一,一对多,多对一,多对多)。 包头大小为8字节。应用:QQ聊天软件,在线视频,网络电话等三次握手与四次握手 HYPERLINK /whuslei/article/details/6667471 /whuslei/article/details/6667471先来看看如何建立连接的首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源
56、。Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了。那如何断开连接呢?简单的过程如下【注意】中断连接端可以是Client端,也可以是Server端。假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说我Client端没有数据要发给你了,但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Se
57、rver端确定数据已发送完成,则向Client端发送FIN报文,告诉Client端,好了,我这边数据发完了,准备好关闭连接了。Client端收到FIN报文后,就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“Server端收到ACK后,就知道可以断开连接了。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?答:因为当Ser
58、ver端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同 步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,你 发的FIN报文我收到了。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠
59、的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。SocketINADDR_ANY选项 网络编程中常用到bind函数,需要绑定IP地址,这时可以设置INADDR_ANY INADDR_ANY就是指定地址为的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。也就是表示本机的所有IP,因为有些机子不止一块网卡,多网卡的情况下,这个就表示所有网卡ip地址的意思。 比如一台电脑有3块网卡,分别连接三个网络,那么这台电脑就有3个ip地址了,如果某个应用程序需要监听某个端口,那他要监听哪个网卡地址的端口呢?如果绑定某个具体的ip地址,你只能监听你所设
60、置的ip地址所在的网卡的端口,其它两块网卡无法监听端口,如果我需要三个网卡都监听,那就需要绑定3个ip,也就等于需要管理3个套接字进行数据交换,这样岂不是很繁琐?所以你只需绑定INADDR_ANY,管理一个套接字就行,不管数据是从哪个网卡过来的,只要是绑定的端口号过来的数据,都可以接收到。 当然, 客户端connect时,不能使用INADDR_ANY选项。必须指明要连接哪个服务器IP。struct sockaddr_in short sin_family; unsigned short sin_port; IN_ADDR sin_addr; char sin_zero8;该结构体用于指定一个s
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025版城市综合体安保服务合同书范本3篇
- 2025版新能源车辆定期维护合同3篇
- 2025版无利息教育培训机构设备购置贷款合同示范文本3篇
- 2025年度个人装修工程售后服务合同
- 二零二五年度车棚租赁与智能交通系统融合合同4篇
- 二零二五年度林业资产评估与交易合同3篇
- 2025版文山冻干三七种植基地农业生态循环经济合作合同3篇
- 2024门窗安装工程节能环保验收合同3篇
- 二零二五年度汽车玻璃更换与售后服务协议范本3篇
- 2025版无房产证房屋买卖合同标准范本精装3篇
- 高中物理竞赛真题分类汇编 4 光学 (学生版+解析版50题)
- 西方经济学-高鸿业-笔记
- 幼儿园美术教育研究策略国内外
- 高中英语选择性必修一单词表
- 物业公司介绍
- 2024届河南省五市高三第一次联考英语试题及答案
- 【永辉超市公司员工招聘问题及优化(12000字论文)】
- 孕妇学校品管圈课件
- 《愿望的实现》交流ppt课件2
- 中国直销发展四个阶段解析
- 2024届浙江省宁波市镇海区镇海中学高一物理第一学期期末质量检测试题含解析
评论
0/150
提交评论