博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
今天指针练习
阅读量:4176 次
发布时间:2019-05-26

本文共 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)

三、

#include 
int 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操作很危险

博主:getmemoryp是形参,是一个指针变量,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个函数哪个能成功进行两个数的交换?#include
using 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/

你可能感兴趣的文章
Yocto tips (4): Yocto 如何确定(找到)一个包的名字
查看>>
start kernel 之后没有任何输出与uboot无法将bootargs传入内核的调查方法与解决之道
查看>>
Yocto tips (5): Yocto如何更改source code的下载与git clone地址
查看>>
Yocto tips (7): Yocto Bitbake的clean与cleanall以及cleansstate的区别
查看>>
Yocto tips (19): Yocto SDK Toolchian的使用
查看>>
Yocto i.MX6 (TQIMX6) (04) : 使用mjpg-streamer做一个WebCam Server
查看>>
Nexus 7 Cyanogenmod OS Compile and errors
查看>>
Yocto tips (20): Yocto中qemu模拟器的使用,以zynq Cortex-A9为例
查看>>
打造嵌入式ARM Linux防火墙:1. iptables基础
查看>>
4G模块SIMCOM7100 LTE在ARM Linux下使用PPPD上网
查看>>
为小米4与小米3 Mi3 Mi4编译Cyanogenmod 12.1与13.0 (CM12与CM13) 的步骤以及错误解决
查看>>
原生Android系统的第一次开机google验证的解决
查看>>
S5P4418与S5P6618的Android boot.img的解压与压缩, Sparse ext4文件系统
查看>>
【EVB-335X-II试用体验】 u-boot与kernel的编译以及本地repo的建立
查看>>
【EVB-335X-II试用体验】 上手试用与资源使用
查看>>
【EVB-335X-II试用体验】 Yocto环境的建立及Rootfs的构建与使用
查看>>
<<C++程序设计原理与实践>>粗读--chapter0 chapter1 chapter2
查看>>
<<C++程序设计原理与实践>>粗读--chapter3 chapter4 Chapter5
查看>>
<<C++程序设计原理与实践>>粗读 -- chapter8 Chapter9
查看>>
Linux Qt程序打包成一个可执行文件
查看>>