指针问题专用贴

指针作为形参肯定有很多都不清楚其中具体的原理,我也是最近摸清了些门道:


下面就用一些例子来给大家说明:

[cpp]  view plain  copy
  1. void myMalloc(char *s) //我想在函数中分配内存,再返回   
  2. {   
  3.     s=(char *) malloc(100);   
  4. }   
  5. void main()   
  6. {   
  7.     char *p=NULL;   
  8.     myMalloc(p); //这里的p实际还是NULL,没有能分配到100字节空间,为什么?   
  9.     if(p)   
  10.        free(p);  
  11. }   

 
基于上面的问题: 因为指针作为形参传递过去的是它的拷贝,只是说他们指向同一块内存地址。 

    上面其实是给他的拷贝体进行了内存地址分配

[cpp]  view plain  copy
  1. void myMalloc(char **s)   
  2. {   
  3.     *s=(char *) malloc(100);   
  4. }   
  5. void main()   
  6. {   
  7.     char *p=NULL;   
  8.     myMalloc(&p); //这里的p可以得到正确的值了   
  9.     if(p) free(p);   
  10. }   

这里为什么能得到正确的值了呢? 

因为这里形参分配的内存其实是给s本身在分配了 ,上面分配的时候用的是(*s)

[cpp]  view plain  copy
  1. #include<stdio.h>   
  2. void fun(int *p)   
  3. {   
  4.     int b=100;   
  5.     p=&b;   
  6. }   
  7. main()   
  8. {   
  9.     int a=10;   
  10.     int *q;   
  11.     q=&a;   
  12.     printf("%d/n",*q);   
  13.     fun(q);   
  14.     printf("%d/n",*q);   
  15.     return 0;   
  16. }   
这里打印的结果为: 10  10

为什么呢? 因为main函数中q指针作为形参传递给了fun函数,fun函数中p为q指针的拷贝体,f

fun函数中的p在函数体中重新指向了整数b的地址。所以q指针本身的值是没有发生变化的


[cpp]  view plain  copy
  1. #include<stdio.h>   
  2. void fun(int *p)   
  3. {   
  4.     *p=100;   
  5. }   
  6. main()   
  7. {   
  8.     int a=10;   
  9.     int *q;   
  10.     q=&a;   
  11.     printf("%d/n",*q);   
  12.     fun(q);   
  13.     printf("%d/n",*q);   
  14.     return 0;   
  15. }   

这里打印的结果是: 10  100

这里可以正确打印我们想要的结果了,因为main函数中的q指针和形参中的p指针都指向了整数a的地址。

fun函数中其实是修改整数a地址中的值,所以在后文中打印指针q中的内容时,也能打印出100了



指针作为函数参数


鉴于很多人对“函数参数传递”这一块不理解,比如问int &a与int*&a的区别等等,我根据自己的理解,以容易理解的方式总结了一下,总共有八种(可能还有)。这只是我能想到了的。请大家继续多多指教,拍砖也可以!
另外声明:为了便于解释,我把讲解都以注释的形式写在程序中了。不习惯的人就迁就看吧!

C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

#include <iostream>
using  namespace  std;
/*为了便于区分讲解,函数中的临时参数标识符不用void fun1(int x,int y)代码中的x,y而用a与b替换
*另外,程序解释不用太多的语法,用通俗的话语依旧可以很有趣。“千言万语不如一个好例子”。下面
*我就遵循这个原则去解释我对问题的理解。
*/
 
/*fun1中的参数ab是在函数fun1调用的时候才分配内存单元,也就是临时变量,在函数调用结束后
*自动销毁。当调用fun1(x,y)时,实际相当于fun1(int a=x,int b=y)或者fun1(int a=*x0,int b=*y0);
*因此ab有自己的内存,通过调用ab也有了自己的值,那么在函数中无论对ab的什么操作,都只影响到ab,
*而与xy无关。
*用一个比喻更好解释:假设我国要引进俄国某个机型的战斗机生产线,那么通过政府(main())批准审核投资
*这个生产线就建造成了(fun1());那么这个生产线与俄国内的同样的生产线之间是相互独立的
*(ab与xy的区别),唯一的联系是:通过政府(main())出钱购买他们的技术或者设配(调用,同时a=x,b=y)
*经过这样之后,我们无论在这个生产线上造什么牛逼的歼十,十一。都与俄国本部的生产线无关了,他们关不
*着也问不着!
*/

void  fun1( int  a, int  b)
{ //这个函数交换数值是:治标不治本
     int  tem;
     tem=a;
     a=b;
     b=tem;
     cout<< "fun1(int a,int b):a=" <<a<< ",b=" <<b<<endl;
}
 
/*fun2数值的交换效果依旧是治标不治本。而且要注意它与fun3的区别,虽然它们形参格式相同
*临时开辟三个临时指针,a、b、tem。同样三者都是局部变量,与函数“老大”“同年同月同日同时生”
*而且“同年同月同日同时死”(很像三国的桃园三结义,都是忠诚的卫士)。fun2中的指针ab存储的
*是x,y的地址,等价于fun2(int *a=&x,int *b=&y)或者fun2(int *a=x0,int *b=y0);fun2中的代码
*实现的是把指针ab存的地址值交换一下,实质是交换了ab指针的指向,因此ab存储的地址中存储
*值还是没有改变。而且也不影响外界指针x0y0的指向,在fun7中会看到与不同
*可以通过fun2中注释掉的语句验证一下
*/
void  fun2( int  *a, int  *b)
{
     //cout<<"a存的地址值"<<a<<"\nb存的地址值"<<b<<endl;
     //cout<<"a指向的值"<<*a<<"\nb指向的值"<<*b<<endl;
     int  *tem;
     tem=a;
     a=b;
     b=tem;
     cout<< "fun2(int *a,int *b):*a=" <<*a<< ",*b=" <<*b<<endl;
     //cout<<"a存的地址值"<<a<<"\nb存的地址值"<<b<<endl;
     //cout<<"a指向的值"<<*a<<"\nb指向的值"<<*b<<endl;
}
 
 
 
/*fun3交换数值的效果才算治本了!
*局部变量问题不再解释。通过临时指针ab中存储的地址,找到真正要交换的xy值,然后把他们交换了!
*公安局喜欢这样干活,他们总是不能直接找到犯罪主谋,但是他们会找到一些知道主谋线索的其他非主谋
*嫌疑犯,那么通过抓住他们然后用刑折磨,这要比直接找主谋要容易些,当然这样做有些曲折(绕弯)!但也很有效
*同理,假设你在家里建一个小金库(这里的xy),那么你要设置好自己的密码(xy的地址),然后死死地
*记在脑海(指针)中。每当想打开看看让自己踏实些,你就要从脑海中找到这个密码然后打开它。然而技术
*在飞跃,假设已有《盗梦空间》中的技术,我想改变你的意念(fun3)或者从梦中偷你的思想或者秘密
*接下来的情节会更精彩...
*/
void  fun3( int  *a, int  *b)
{
     int  tem;
     tem=*a;
     *a=*b;
     *b=tem;
     cout<< "fun3(int *a,int *b):*a=" <<*a<< ",*b=" <<*b<<endl;
}
 
 
/*fun4表述的是“引用”
*引用的通俗解释:相当于一个人有多个名字或者外号一样。无论你叫他哪个名字,他都应。
*因此引用很像:一个变量有多个标识符(这样说稍微有些不当)。如果a是变量b的引用,那么ab是同一对象
*通过调用fun4(x,y)或者fun4(*x0,*y0).那么临时变量ab分别是xy与*x0与*y0的引用了。对ab操作就相当于在
*main中直接操作xy
*/
void  fun4( int  &a, int  &b)
{
     int  tem;
     tem=a;
     a=b;
     b=tem;
     cout<< "fun4(int &a,int &b):a=" <<a<< ",b=" <<b<<endl;
}
 
 
/*fun5参数采用指向指针的指针,这比fun2,fun3还要绕弯子,但这里依旧要注意fun5与fun7的区别
*fun5依旧治标不治本,fun6才能治本,他们实质和fun2与fun3类似,只不过比它们要多一层指针
*既然多一层指针那么在调用的时候传递的实参就必须是指针的地址,而不能是别的。等价于
*fun5(int **a=&x0,int **b=&y0)
*换用上面的比喻:相当于在《盗梦空间》里进入第二层梦里偷意念了。c++里没有拓展到第三,四层梦里
*去偷东西!也就是没有***a或者*****a。因为两层处理的很好了!
*/
void  fun5( int  **a, int  **b)
{
     int  **tem;
     tem=a;
     a=b;
     b=tem;
     cout<< "fun5(int **a,int **b):**a=" <<**a<< ",**b=" <<**b<<endl;
}
 
 
/*fun6和fun3有相同的实质,只是多一层指针
*/
void  fun6( int  **a, int  **b)
{
     int  tem;
     tem=**a;
     **a=**b;
     **b=tem;
     cout<< "fun6(int **a,int **b):**a=" <<**a<< ",**b=" <<**b<<endl;
}
 
/*fun7实现与fun2不同效果,上面我已经提到了。
*fun2结果是函数内部的交换不会影响到外面指针的指向,但是这里不同了,fun7函数实现了指针x0y0
*指向的交换。但却没有影响到xy的值的交换。就因为多了一个“引用(&)”。而fun2中是开辟的临时指针变量
*明白引用就能明白这里的效果。
*/
void  fun7( int  *&a, int  *&b)
{
     int  *tem;
     tem=a;
     a=b;
     b=tem;
     cout<< "fun7(int *&a,int *&b):*a=" <<*a<< ",*b=" <<*b<<endl;
}
 
/*这里达到的效果同fun3,二者一定程度上可以互相替换,只不过调用时有些区别!
*/
void  fun8( int  *&a, int  *&b)
{
     int  tem;
     tem=*a;
     *a=*b;
     *b=tem;
     cout<< "fun8(int *&a,int *&b):*a=" <<*a<< ",*b=" <<*b<<endl;
}
int  main()
{
     int  x=13,*x0=&x,&x1=x;
     int  y=250,*y0=&y,&y1=y;
 
     cout<< "\n调用前main():x=" <<x<< ",y=" <<y<<endl;
     fun1(x,y);
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
 
     //fun1(&x,&y);//错误
 
     cout<< "\n调用前main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     fun1(*x0,*y0); //这与上面的fun1调用没有区别
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
 
 
 
     cout<< "\n调用前main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     fun2(x0,y0); //把指针的地址传递过去,
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
 
     cout<< "\n调用前main():x=" <<x<< ",y=" <<y<<endl;
     fun2(&x,&y); //把指针的地址的副本传递过去(按值传递),依旧不会影响原来的指针x0y0的指向
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
 
 
 
     cout<< "\n调用前main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     fun3(x0,y0);
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
 
     cout<< "\n调用前main():x=" <<x<< ",y=" <<y<<endl;
     fun3(&x,&y);
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
 
 
 
     cout<< "\n调用前main():x=" <<x<< ",y=" <<y<<endl;
     fun4(x,y);
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
 
     cout<< "\n调用前main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     fun4(*x0,*y0);
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
 
 
     //fun5(&x,&y);//错误
 
     cout<< "\n调用前main():x=" <<x<< ",y=" <<y<<endl;
     fun5(&x0,&y0);
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
 
     cout<< "\n调用前main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     fun6(&x0,&y0);
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
 
 
//这里之所以把fun8调用放在fun7之前,是因为如果fun7在fun8前,那么fun7调用后x0指向y,y0指向x,
//再调用fun8得到的结果不便于理解。
     cout<< "\n调用前main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     fun8(x0,y0);
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
 
 
 
     cout<< "\n调用前main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     fun7(x0,y0);
     cout<< "调用后main():*x0=" <<*x0<< ",*y0=" <<*y0<<endl;
     cout<< "调用后main():x=" <<x<< ",y=" <<y<<endl;
 
     //fun7(&x,&y);//错误
 
    system ( "pause" );
    return  0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
电子图书资源服务系统是一款基于 Java Swing 的 C-S 应用,旨在提供电子图书资源一站式服务,可从系统提供的图书资源中直接检索资源并进行下载。.zip优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人系统开发经验充足,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(若有),项目具体内容可查看下方的资源详情。 【附带帮助】: 若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步。 【本人专注计算机领域】: 有任何使用问题欢迎随时与我联系,我会及时解答,第一时间为你提供帮助,CSDN博客端可私信,为你解惑,欢迎交流。 【适合场景】: 相关项目设计中,皆可应用在项目开发、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面中 可借鉴此优质项目实现复刻,也可以基于此项目进行扩展来开发出更多功能 【无积分此资源可联系获取】 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。积分/付费仅作为资源整理辛苦费用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值