指针作为形参肯定有很多都不清楚其中具体的原理,我也是最近摸清了些门道:
下面就用一些例子来给大家说明:
- void myMalloc(char *s) //我想在函数中分配内存,再返回
- {
- s=(char *) malloc(100);
- }
- void main()
- {
- char *p=NULL;
- myMalloc(p); //这里的p实际还是NULL,没有能分配到100字节空间,为什么?
- if(p)
- free(p);
- }
基于上面的问题: 因为指针作为形参传递过去的是它的拷贝,只是说他们指向同一块内存地址。
上面其实是给他的拷贝体进行了内存地址分配
- void myMalloc(char **s)
- {
- *s=(char *) malloc(100);
- }
- void main()
- {
- char *p=NULL;
- myMalloc(&p); //这里的p可以得到正确的值了
- if(p) free(p);
- }
这里为什么能得到正确的值了呢?
因为这里形参分配的内存其实是给s本身在分配了 ,上面分配的时候用的是(*s)
- #include<stdio.h>
- void fun(int *p)
- {
- int b=100;
- p=&b;
- }
- main()
- {
- int a=10;
- int *q;
- q=&a;
- printf("%d/n",*q);
- fun(q);
- printf("%d/n",*q);
- return 0;
- }
为什么呢? 因为main函数中q指针作为形参传递给了fun函数,fun函数中p为q指针的拷贝体,f
fun函数中的p在函数体中重新指向了整数b的地址。所以q指针本身的值是没有发生变化的
- #include<stdio.h>
- void fun(int *p)
- {
- *p=100;
- }
- main()
- {
- int a=10;
- int *q;
- q=&a;
- printf("%d/n",*q);
- fun(q);
- printf("%d/n",*q);
- return 0;
- }
这里打印的结果是: 10 100
这里可以正确打印我们想要的结果了,因为main函数中的q指针和形参中的p指针都指向了整数a的地址。
fun函数中其实是修改整数a地址中的值,所以在后文中打印指针q中的内容时,也能打印出100了
指针作为函数参数
鉴于很多人对“函数参数传递”这一块不理解,比如问int &a与int*&a的区别等等,我根据自己的理解,以容易理解的方式总结了一下,总共有八种(可能还有)。这只是我能想到了的。请大家继续多多指教,拍砖也可以!
另外声明:为了便于解释,我把讲解都以注释的形式写在程序中了。不习惯的人就迁就看吧!
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;
}
|