




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、有关C/C+指针的经典面试题 (2010-06-06 00:26:50)转载标签: 杂谈分类: C/C+C语言为何如此长寿并实用?C+为什么有那么多精彩?指针可以说是C/C+中的灵魂所在,虽然早期中pascal也有指针,但是和C/C+比起来不是一个级别的.今天为大家深入浅出的解析一下指针的有关笔试,面试题.所有题目来源网络,分析是我写的.0.预备知识,最基础的指针其实最基础的指针也就应该如下面代码:int a;int* p=&a;也就是说,声明了一个int变量a,然后声明一个int 的指针,*p指向a的地址,&也就是取地址符号,而*是指针中取内容的
2、符号,仅仅在声明的时候标记这个变量是指针.可能有点绕口,但是看代码来的容易的多. 1.与const在一起的时候常常声明的时候会让人一头雾水,比如下面的声明,均是声明一个char* p的指针:char * const p; / 指针不可改,也就说指针只能指向一个地址,不能更改为其他地址char const * p; / 所指内容不可改,也就是说*p是常量字符串char const * const p; / 内容和指针都不能改const char * const p; / 同上.内容和指针不能改额.别晕,别晕
3、.其实方法很简单.你别真死记硬背.其实可以以*为分界符,在*左边有const就说明内容不能改,在*右边就说明指针不能改,而左边的char和const顺序是不要紧的.呵呵.你也可以理解成const是修饰后面的,正常顺序应该这样:const char * const p; 是不是看起来简单了? 2.忽悠人的陷阱,str和*str的区别先告诉你哦,下面的题目可是陷阱啊.说说程序结果.char str1 = “abc”;char str2 = “abc”;const char str3 = “abc”;const char str4 = “abc”;const char *str5 = “a
4、bc”;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;怎么样?都输出true?那显然你中标了.而且cout输出bool值的时候,就算全是真也应该都输出1啊.4个1?那也
5、不对.答案是0011,不信你试试.为什么呢?其实都说了这题是个大陷阱,因为这题根本不是比较字符串内容!而是比较字符串的地址.哦.恍然大悟.那为什么前两个是假呢?因为这可是说是一个深拷贝/浅拷贝的问题.当字符串是数组形式声明并初始化,编译器认为是新数组,分配新空间,但不是深拷贝,因为根本就不算拷贝.而如果是相同的字符串,用指针声明,那就是比较如果有一样的字符串,就直接把新指针指过去,这是正宗的浅拷贝.哇哈.你就中计了. 3.str用sizeof判断会出错么?应该说我们常常用指针有很多时候是解决字符串的问题,一般我们用strlen,这当然没有问题,但是要你编一个呢?看看下面这个MyStr
6、len有问题么?int MyStrlen(char str) return (int)(sizeof(str)-1);呵呵.咱们上当过一次.这个当然也是不对的.不错.这个函数是错的.为什么呢?首先,可以告诉你,无论何时,返回的总是3.额.是不是很奇怪,为什么不是数组长度呢?str不是char数组指针么?不错.确实是数组的指针,但是,当用函数传递的数组指针的时候就自动退化为指针了,而指针的长度是4,你减去1了自然就是3了.但是如果按照下面代码就可以得到正常的值.char str="hello world"int len=sizeo
7、f(str)-1; /记得减1哦,最后有'0'结尾cout<<len;这样输出的是正常值,也就是你所希望的11; 4.注意数组指针和指针继续上面的话题,刚刚提到了数组指针和指针,现在看看下面这端程序代码:int a5=1,2,3,4,5;int *ptr=(int *)(&a+1);cout<<*(a+1)<<*(ptr-1);呵呵.BaihowFF总是给陷阱.肯定不是想当然的说就是21.确实.答案是25.额.奇怪吧.为什么呢?首先,a是一个数组,所以编译器解释&a就是a的全部长度,
8、就是说(&a+1)也就是说移动了一个数组,指向了并不存在的a5,所以ptr-1才会指向a数组的最后一个元素a4,而a+1和a1是一样的.所以答案是25,如果你去掉了(&a+1)的括号,那么答案就是想当然的21了.呵呵.很微妙吧. 5.注意指针要分配给足够的空间新手在刚刚接触指针的时候经常会忘记给指针分配空间,直接用肯定是有问题的,那么下面的程序呢?char a;char *str=&a;strcpy(str,”hello”);cout<<str;BaihowFF是坏蛋.总会下套.呵呵.确实是圈套.这段程序能够输出hello,但是输出后就崩溃了.原因
9、就在你分配str指针的时候仅仅给了1字节的空间,但是你拷贝了6字节过去(不要忘记了最后的'0'结束).运行输出后程序因为访问了没有分配的呵呵空间,当然崩溃了.如果你只strcpy(str,"");那程序是可以正常运行的. 6.小心编译器的指针字符串初始化经常我们想自己处理字符串,但是像下面的初始化是很危险的!char* s="AAA"cout<<s<<endl;s0='B'cout<<s<<endl;你可以拿这段程序去编译.没错!编译器报告正常!.这是最要命的.其实
10、程序不能运行的.输出AAA后就崩溃了.为什么?因为当你在第一句初始化的时候,编译器就认为这是个字符串常量了.再做数组操作的时候肯定错了罗.最好的习惯是声明一个指针,用new分配空间,然后用库函数操作,比如strcpy,strcat等等. 7.让人一眼看上去迷糊的函数指针看看这句代表什么意思?int (*s10)(int);咦.这是什么?其实这是一个函数指针数组,指向了一组int fun(int)的函数,第一眼确实让人有点迷糊.但是请习惯这样. 8.注意函数传递指针的时候是副本副本?又下副本?.汗.老兄.不是这个意思.别沉浸在WOW里了啊.看看下面程序的问题:void Get
11、Memory(char *p)p=new char100;strcpy(p,"hello world");void main(void)char *str=NULL;GetMemory(str);cout<<str;delete str;str=NULL;当然了.喜欢下套的BaihowFF又给了错程序.错在哪呢?看上去是对的,而且编译器编译也正确啊.怎么就是不能通过呢?而且还崩溃了.好费解吧.其实原因很简单.GetMemory这个函数出问题了!函数参数是不能传递分配空间的.因为传递过去实际上是一个副本p.不能返回的.而且你在delete那就是件很危险的事情.因为
12、压根没有内容.那我实在想这样用函数分配怎么办呢?像下面这样改一下就ok了:void GetMemory(char *p) / 改成晦涩难懂的指针的指针*p=new char100; /给*p的分配地址strcpy(*p,"hello world"); / 拷贝内容到*pvoid main(void)char *str=NULL;GetMemory(&str); /这地方取地址cout<<
13、;str;delete str;str=NULL;这样就能正常工作了,但是看起来好别扭啊.嗯.确实.但是还可以用其他方法哦.你想想.肯定有办法的.9.请时刻记住要初始化字符串嗯.这点大家都知道.那你猜猜下面的程序结果是多少?char a10;cout<<strlen(a)<<endl;答案应该让你以外.竟然是15.没道理吧?!其实strlen函数的结果和是否初始化有关的.虽然你分配了空间.但是没有初始化.库函数会出错的.sizeof不受影响.切忌初始化哦. 10.小括号,大区别看看这两端声明,有什么不同?我直接在注释里告诉你答案吧.这样好看点.char (*s
14、tr)20; /str是一个数组指针,即指向数组的指针char *str20; /str是一个指针数组,其元素为指针型数据千万别小看括号哦.区别大了吧.1.char * const p;char const * pconst char *p上述三个有什么区别?char * const p; /常量指针,p的值不可以修改char const * p;/指向常量的指针,指向的常量值不可以改const char *p; /和char const *p2.char str1 = “abc”;char str2 = “abc
15、”;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 )
16、<< endl;打印结果是什么?解答:结果是:0 0 1 1str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域3. 以下代码中的两个sizeof用法有问题吗?void UpperCase( char str ) / 将 str 中的小写字母转换成大写字母for( size_t i=0; i<sizeof(str)/sizeof(str0); +i )if( a<=stri && stri<=z )stri -= (a-'A );char str =
17、“aBcDe”;cout << “str字符长度为: ” << sizeof(str)/sizeof(str0) << endl;UpperCase( str );cout << str << endl;答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。4.main
18、()int a5=1,2,3,4,5;int *ptr=(int *)(&a+1);printf(“%d,%d”,*(a+1),*(ptr-1);输出结果是什么?答案:输出:2,5*(a+1)就是a1,*(ptr-1)就是a4,执行结果是2,5&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)int *ptr=(int *)(&a+1);则ptr实际是&(a5),也就是a+5原因如下:&a是数组指针,其类型为 int (*)5;而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。a是长
19、度为5的int数组指针,所以要加 5*sizeof(int)所以ptr实际是a5但是prt与(&a+1)类型是不一样的(这点很重要)所以prt-1只会减去sizeof(int*)a,&a的地址是一样的,但意思不一样a是数组首地址,也就是a0的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a1,&a+1是下一个对象的地址,即a5.5.请问以下代码有什么问题:int main()char a;char *str=&a;strcpy(str,”hello”);printf(str);return 0;答案:没有为str分配内存空间,
20、将会发生异常。问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。6. char* s=”AAA”;printf(“%s”,s);s0=B'printf(“%s”,s);有什么错?答案:“AAA”是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s=”AAA”;然后又因为是常量,所以对是s0的赋值操作是不合法的。7. int (*s10)(int) 表示的是什么?答案:int (*s10)(int) 函数指针数组,每个指针指向一个int func(int param)的函数。8.有以
21、下表达式: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=0x321f;答案:*c 这是个什么东东,禁止*d 说了是const, 禁止e = &a 说了是const 禁止const *f const =&a; 禁止9.#include <stdio.h>#include <stdlib.h>voi
22、d 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中的malloc 不能返回动态内存, free()对str操作很危险博主:getmemory中p是形参,是一个指针变量,getmemory(str)调用后,传入的是指针变量保存的对象地址,p=(char *) malloc(100)实
23、际上是把申请的动态内存空间的首地址付给p指向的地址(即str指向的地址null),这个是错误的。应该修改成指向指针的指针void getmemory(char *p),这样malloc返回的地址付给*p(即str变量本身)。10.char szstr10;strcpy(szstr,”0123456789);产生什么结果?为什么?答案:长度不一样,会造成非法的OS11.要对绝对地址0×100000赋值,我们可以用(unsigned int*)0×100000 = 1234;那么要是想让程序跳转到绝对地址是0×100000去执行,应该怎么做?答案:*(void (*)
24、( )0×100000 ) ( );首先要将0×100000强制转换成函数指针,即:(void (*)()0×100000然后再调用它:*(void (*)()0×100000)();用typedef可以看得更直观些:typedef void(*)() voidFuncPtr;*(voidFuncPtr)0×100000)();12. 分析下面的程序:void GetMemory(char *p,int num) &
25、#160; /p,指向指针的指针,*p,p指向的指针(即str),*p,最终的对象,str指向的单元*p=(char *)malloc(num); /申请空间首地址付给传入的被p指向的指针,即str int main()char *str=NULL;GetMemory(&str,100); /传入指针变量本身的地址strcpy(str,”hello”);free(str);if(str!
26、=NULL)strcpy(str,”world”); printf(“n str is %s”,str); 软件开发网 getchar(); 问输出结果是什么?答案:输出str is world。free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。这是因为,进程中的内存管理一般不是由操作系统完成的,而
27、是由库函数自己完成的。当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存 前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。-13.char a10;strlen(a)为什么等于15?#include “stdio.h”#include “string.h”void main()char aa10;printf(“%d”,strlen(aa);答案:sizeof()和初不初始化,没有关系;strlen()和初始化有关。14.cha
28、r (*str)20;/*str是一个数组指针,即指向数组的指针*/char *str20;/*str是一个指针数组,其元素为指针型数据*/15.#include<iostream.h>#include <string.h>#include <malloc.h>#include <stdio.h>#include <stdlib.h>#include <memory.h>typedef struct AAint b1:5;int b2:2;AA;void main()AA aa;char cc100;strcp
29、y(cc,”0123456789abcdefghijklmnopqrstuvwxyz”);memcpy(&aa,cc,sizeof(AA);cout << aa.b1 <<endl;cout << aa.b2 <<endl;输出结果是多少?答案:-16和首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.经过strcpy和memcpy后,aa的4个字节所存放的值是: 0,1,2,3的ASC码,即00110000,00110001,00110010,00110011所以,最后一步:显示的是这个字节的前位,和 之后的位分别
30、为:10000,和01,因为int是有正负之分-16 试题三: 以下代码有哪些错误或不足之处void GetMemory( char *p, int num )*p = (char *) malloc( num ); void Test( void )char *str = NULL;GetMemory( &str, 100 );strcpy( str, "hello" );printf( str );解答:存在2处问题:本题中的Test函数中未对malloc的内存进行释放。本题中的GetMemory避免了试题一的问题,传入GetMe
31、mory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句*p = (char *) malloc( num );后未判断内存是否申请成功,应加上:if ( *p = NULL )./进行申请内存失败处理17 试题四:找出下面代码的不足或错误之处void Test( void )char *str = (char *) malloc( 100 );strcpy( str, "hello" );free( str );. /省略的其它语句解答:存在2处问题:试题四存在与试题三同样的问题,在执行char *str = (char *) malloc(10
32、0);后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:str = NULL;18 判断输出是乱码 还是字符或是字符串 还是表示地址值 ? 如果是地址值就用0xF1247000代表.char类型指针的输出 标准库cout输出时,无法判断指针的类型,如下面程序所示:char ch = 'a'int num = 1;char *pch = &ch;int *pnum = #std:
33、cout<<pch<<std:endl; /输出:乱码std:cout<<(void*)pch<<std:endl; /输出:0xF1247000std:cout<<pnum<<std:endl;/输出:0xF1247004std:cout<<(void*)pnum<<std:endl; /输出:0xF12470004char *pch2="Hello"std:cout<<pch2<&
34、lt;std:endl;/输出:Hellostd:cout<<(void*)pch2<<std:endl;/输出:16进制地址 对于字符型指针,要用void*进行类型转换后,才能输出其地址,地址以16进制数的格式显示32位地址值。若不进行转换,cout会按照char类型解析指针内容。若是一个字符,没有'0'结束符,输出乱码;若是字符串,则输出字符串内容。 除了字符指针,其他指针都可以直接用cout语句来输出地址值。无须进行指针类型转换。19 指针与引用的区别?答案
35、:(1)非空区别。在任何情况下都不能使用指向空值的引用。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。(2)合法性区别。在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。(3)可修改区别。指针与引用的另一个重要的区别是指针可以被重新赋值以指向另一个不同对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指
36、定的对象其内容可以改变。思路: 遇到次类型的题目 一定要想想他们的特点是什么 抓住特点进行分析 想想他们的功能是什么 进行比较20:下面5个函数哪个能成功进行两个数的交换?cpp view plain copy 1. #include<iostream> 2. using namespace std; 3. 4. void&
37、#160;swap1(int p,int q) 5. 6. int temp; 7. temp=p; 8. p=q; 9. q=temp; 10. 11. 12. void
38、swap2(int *p,int *q) 13. 14. int *temp; 15. *temp=*p; 16. *p=*q; 17. *q=*temp; 18. 19. 20. v
39、oid swap3(int *p,int *q) 21. 22. int *temp; 23. temp=p; 24. p=q; 25. q=temp; 26. 27. 28
40、. void swap4(int *p,int *q) 29. 30. int temp; 31. temp=*p; 32. *p=*q; 33. *q=temp; 34. 35.
41、160;36. void swap5(int &p,int &q) 37. 38. int temp; 39. temp=p; 40. p=q; 41. q=temp; 42. 43.
42、0; 44. int main () 45. 46. int a=1,b=2; 47. /swap1(a,b); 48. /swap2(&a,&b); 49. /swap3(&a,&b);
43、50. /swap4(&a,&b); 51. /swap5(a,b); 52. cout << "a:"<< a <<endl; 53. cout << "b:"<<
44、 b <<endl; 54. 55. return 0; 56. 解析:这道题考察的是参数传递、值传递、指针传递(地址传递)和引用传递。 swap1传递的是值的副本,在函数中只是修改了形参p、q(实际是a、b的一个拷贝),p、q的值确实交换了,但是它们是局部变量,不会影响到主函数a和 b 。当函数swap1生命周期结束时,p、q所在的栈也就被删除了。
45、60; swap2传递的是一个地址进去,在函数体内的形参*p、*q是指向实际的参数a、b地址的两个指针。 这里要注意: int *temp; *temp=*p; 是不符合逻辑的,i
46、nt *temp新建了一个指针(但是没分配内存)。*temp=*p不是指向而是拷贝。把*p所指向的内存的值(也就是a 的值)拷贝到*temp所指向内存里了。但是int *temp不是不分配内存吗?的确不分配,于是系统在拷贝时临时给了一个随机地址,让它存值。分配的随机地址是个“意外”,且函数结束后不回收,造成内存泄漏。 swap3传递的是一个地址,在函数体内的参数*p、*q是指向实际参数a、b地址的两个指针。 这里要注意:
47、0; int *temp; temp=p; int *temp新建了一个指针(但是没分配内存)。temp=p是指向而不是拷贝。temp指向了*p所指向的地址(也就是a )。而代码: int *temp; q=temp; 但是函数swa
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 个人资金借贷合同范例
- 医疗设备电气安全检查的重要性
- 企业内部实验室应急管理与实施案例
- 企业用房购买合同范例
- 医疗数据共享的保障者-区块链技术的角色与挑战
- 医疗技术IP保护的挑战与对策分析
- 主机租赁服务合同范例
- M视域下浅析优化患方对诊疗记录保密的措施与问题应对
- 个人猪场租赁合同范例
- 公共服务合同范例
- 水质对干豆腐品质的影响机制及调控技术
- LY/T 2676-2016半干旱地区灌木林平茬与复壮技术规范
- 装配式混凝土结构的构件安装分项工程(验收批)质量验收记录表
- 2021年中原工学院辅导员招聘笔试试题及答案解析
- 作业许可检查表
- 湘美版美术三年级下册 《渔家乐-蟳埔情》课件(共20张PPT)
- 农产品集中交易市场等级技术规范-编制说明
- 张京16分钟中英文对照翻译稿
- 武汉绿地中心项目技术管理策划书(48页)
- 油田相关业务的税制及税率
- 北师大版物理八年级下册课课练:专题训练 透镜的相关作图(含答案)
评论
0/150
提交评论