本文共 5706 字,大约阅读时间需要 19 分钟。
一、
char *const p; //p不可改变
char const *p; //*p不可改变
const char *p; //*p不可改变,同上
刚开始想的既然p
不可变,那么我先定义p = &a
, 再定义p = &b
,自然会出错,确实是这样。 那*p
不可变,我定义p = &a
, 再定义p = &b
,也会出错咯,因为它的*p
还是变了嘛! 同样,我定义p = &a
,再改变a
的值,应该也会出错吧! 但并不是这样。 只有你写*p = XXX
的时候,编译器才会报错。其他间接改变*p
的方式都不会报错。 int main(){ int a[5] = { 1, 2, 3, 4, 5}; int *ptr = (int *)(&a + 1); printf("%d %d\n", *(a + 1), *(ptr - 1)); return 0;}
嗯,刚开始想这段代码输出结果应该是2 1,但又不是。
输出结果是2 5,很惊讶。 解释: &a是数组指针,其类型为 int (*)[5]; 而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。 a是长度为5的int数组指针,所以要加 5*sizeof(int) 所以ptr实际是a[5] 但是prt与(&a+1)类型是不一样的(这点很重要) 所以prt-1只会减去sizeof(int*)。 **a,&a的地址是一样的,但意思不一样: a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址, a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5]。**二、
#incldue#include int main(){ char a; char *str = &a; strcpy(str, "hello"); printf(str); return 0;}
这一个比较好判断。为str分配的空间不足,因为越界进行内在读写导致程序崩溃Segmentation fault(core dumped)
。
三、
#includeint main(){ char *str = "AAA"; printf("%s", str); str[0] = 'B'; printf("%s", str); return 0;}
因为str是一个字符串常量,所以无法对其进行部分修改,即对str[0]的赋值操作是非法的。运行时Segmentation fault(core dumped)
。
四、
int (*s[10])(int)表示的是什么?
emmmm,看完答案豁然开朗(笑)。首先是一个指针数组,这个指针数组里包含是个指针,每个指针都指向一个int func(int param)
的函数。
五、
有以下表达式:int a = 248,b = 4;int const c = 21;const int *d= &a;int *const e= &b;int const *f = &a;请问下列表达式哪些会被编译器禁止?为什么?*c = 32; d = &b; *d = 43; e = 34; e = &a; f = 0x321f;
哈哈,对于*c
竟没反应过来,不知道怎么回事。
*c
是什么东东,禁止! *d = 43
,禁止! e = 34
,禁止! e = &a
,禁止! f = 0x321f
,禁止!(这一个有待讨论,答案说禁止,但我的编译器仅仅是警告 warning: assignment makes pointer from integer without a cast [-Wint-conversion]
六、
#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
中的malloc
不能返回动态内存, free()
对str
操作很危险
getmemory
中p
是形参,是一个指针变量,getmemory(str)
调用后,传入的是指针变量保存的对象地址,p=(char *) malloc(100)
实际上是把申请的动态内存空间的首地址付给p
指向的地址(即str
指向的地址Null
),这个是错误的。应该修改成指向指针的指针void getmemory(char **p)
,这样malloc
返回的地址赋给*p
(即str
变量本身)。 七、
char szstr[10];strcpy(szstr,”0123456789″);产生什么结果?为什么?
答案:长度不一样,会造成非法的OS。
emmmm,但我执行的时候没错误,也能输出str
。 八、
要对绝对地址0×100000赋值,我们可以用(unsigned int*)0×100000 = 1234;那么要是想让程序跳转到绝对地址是0×100000去执行,应该怎么做?
答案:*((void (*)( ))0×100000 ) ( );
0×100000
强制转换成函数指针,即: (void (*)())0×100000
然后再调用它: *((void (*)())0×100000)();
用typedef
可以看得更直观些: typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0×100000)();
冏,这个目前还看不懂。。。 九、
#include#include void GetMemory(char **p, int num);int main(){ char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); free(str); if (str != NULL) strcpy(str, "world"); printf("str is %s\n", str); return 0;}void GetMemory(char **p, int num){ *p = (char *)malloc(num);}
答案:输出str is world
。
free
只是释放的str指向的内存空间,它本身的值还是存在的。所以free
之后,有一个好的习惯就是将str = NULL
。 此时str
指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的, 尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world
来。 这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。 当你malloc
一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存 前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。 额,不知道为什么我执行时是Segmentation fault (core dumped)
。 十、
char (*str)[20];/*str是一个数组指针,即指向数组的指针.*/char *str[20];/*str是一个指针数组,其元素为指针型数据.*/
嗯,其实这个也挺好理解的。比如char *str
你可以类似char a
的这样来思考。char a
是个char
型的变量,那char *str
就是个char *
型的变量啊。不要被它的书写格式搞晕(*
和str
连在一起)。
十一、
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传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句p = (char ) malloc( num ); 后未判断内存是否申请成功,应加上if ( *p == NULL ){ ...//**进行申请内存失败处理**}
十二、
void Test( void ){ char *str = (char *) malloc( 100 ); strcpy( str, "hello" ); free( str ); ... //省略的其它语句} 代码有什么不足或错误?
存在2处问题:
在执行char str = (char ) malloc(100); 后未进行内存是否申请成功的判断; 另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:str = NULL;
十三、
指针与引用的区别:
答案:
(1)非空区别。在任何情况下都不能使用指向空值的引用。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引 用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。 (2)合法性区别。在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。 (3)可修改区别。指针与引用的另一个重要的区别是指针可以被重新赋值以指向另一个不同对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。 思路:遇到这种类型的题目,一定要想想他们的特点是什么,抓住特点进行分析;想想他们的功能是什么,进行比较。十四、
下面5个函数哪个能成功进行两个数的交换?#includeusing namespace std;void swap1(int p,int q){ int temp; temp=p; p=q; q=temp;}void swap2(int *p,int *q){ int *temp; *temp=*p; *p=*q; *q=*temp;}void swap3(int *p,int *q){ int *temp; temp=p; p=q; q=temp;}void swap4(int *p,int *q){ int temp; temp=*p; *p=*q; *q=temp;}void swap5(int &p,int &q){ int temp; temp=p; p=q; q=temp;}int main (){ int a=1,b=2; //swap1(a,b); //swap2(&a,&b); //swap3(&a,&b); //swap4(&a,&b); //swap5(a,b); cout << "a:"<< a <
解析:这道题考察的是参数传递、值传递、指针传递(地址传递)和引用传递。
swap1传递的是值的副本,在函数中只是修改了形参p、q(实际是a、b的一个拷贝),p、q的值确实交换了,但是它们是局部变量,不会影响到主函数a和 b 。当函数swap1生命周期结束时,p、q所在的栈也就被删除了。 swap2传递的是一个地址进去,在函数体内的形参*p
、*q
是指向实际的参数a、b地址的两个指针。 这里要注意: int *temp;
*temp=*p;
是不符合逻辑的,int *temp
新建了一个指针(但是没分配内存)。*temp=*p
不是指向而是拷贝。把*p
所指向的内存的值(也就是a 的值)拷贝到*temp
所指向内存里了。但是int *temp
不是不分配内存吗?的确不分配,于是系统在拷贝时临时给了一个随机地址,让它存值。分配的随机地址是个“意外”,且函数结束后不回收,造成内存泄漏。 swap3传递的是一个地址,在函数体内的参数*p、*q是指向实际参数a、b地址的两个指针。 这里要注意: int *temp;
temp=p;
int *temp
新建了一个指针(但是没分配内存)。temp=p
是指向而不是拷贝。temp指向了*p所指向的地址(也就是a )。而代码: int *temp;
q=temp;
但是函数swap3不能实现两数的交换,这是因为函数体内只是指针的变化,而对地址中的值却没有变化。 swap4可以实现两数的交换,因为它修改的是指针所指向地址中的值。 swap5函数与swap4相似,是一个引用传递,修改的结果直接影响实参。 答案: swap4 函数和 swap5 函数。 嗯,练习这位博主大大的题,感谢!
转载地址:http://cptai.baihongyu.com/