深⼊研究:对变量以及指针重新赋值过程中原本的地址是否会改
变。(按值传递机制的深⼊)
在研究按值传递机制时,发现⼀些模糊的概念。就是在对⼀个原本的(指针)变量重新给定另外⼀个值时,会不会改变这个变量原本所在的内存位置(即地址)。因此,决定深⼊研究⼀下。⽽且这也是必要的。
1. 给⼀个变量重新赋值时,地址的变化
1//验证变量在被赋值(以及被重赋值)时原本分配的内存地址是否会改变。
2
3  #include <stdio.h>
4
5int main(void)
6  {
7int a;//声明a,即已经给a分配⼀个内存地址
8      printf("声明a时的地址:%p\n", &a);
9
10      a = 1;  //初始化a
11      printf("The address of origin: %p\n", &a);//输出a=1时的地址
12
13//int a = 2; ----> 不能以这种⽅式对a重新赋值. 否则会产⽣错误:‘a’重定义
14      scanf("%d", &a);//对a重新赋值。
15      printf("The address of later: %p\n", &a);//被重新赋值后的地址
16
17return0;
18  }
运⾏结果:声明a时的地址:0x7ffc3cabc31c
              The address of origin: 0x7ffc3cabc31c
2  ->这个2是我输⼊的。
The address of later: 0x7ffc3cabc31c
结论:在声明时给变量a划分的地址,会在变量作⽤域内⼀直保持不变。原本a=1时,地址是xxx,在第a重新赋值时,会先将a内的数删除,然后再将新的数放进去,这个新的数的地址还是xxx。即地址⼀直不变,变得是这个地址⾥的数。(就像租房,有效期内房⼦⼀直不变,变的是住的⼈)
2. 在对指针指向的地址重新赋值时,指针所指向的地址是否会改变?
1//验证对指针所指向的值重新赋值时,指针是否会改变。
2  #include <stdio.h>
3
4int main(void)
5  {
6int *p = NULL;
7int a = 1;
8      printf("p指针存储的地址:%p\n", p);
9      printf("p指针的地址:%p\n", &p);
10
11      p = &a;
12      printf("p指针存储的a地址:%p\n", p);
13      printf("p指针的地址:%p\n", &p);
14
15      *p = 2;
16      printf("p指针存储的对a重新赋值时地址:%p\n", p);
17      printf("p指针的地址:%p\n", &p);
18
19int b = 2;
20      p = &b;
21      printf("p指针存储的b地址:%p\n", p);
22      printf("p指针的地址:%p\n", &p);
23
24return0;
25  }
运⾏结果:p指针存储的地址:(nil)
p指针的地址:0x7ffe95265668
p指针存储的a地址:0x7ffe95265664
p指针的地址:0x7ffe95265668
p指针存储的对a重新赋值时地址:0x7ffe95265664
p指针的地址:0x7ffe95265668
p指针存储的b地址:0x7ffe95265660
p指针的地址:0x7ffe95265668
分析:有点乱,但没关系,⼀句⼀句看。在将指针指向NULL时,实质上并没有给指针p分配存储的地址。即p不指向任何地址。所以输出了:p指针存储的地址:(nil)。⽽p本⾝的地址(即存储p的地址)为0x7ffe95265668,在整个main函数内,存储p指针的地址其实是⼀直不变的。这在第1点可以知道原因。p = &a这句是将p指向a所在的地址。*p = 2这句是通过*p对a重新赋值。期间,p存储的地址保
持⼀致,即:0x7ffe95265664。p = &b这⼀句将p重新指向⼀个新的变量了。打个⽐⽅:(指针p包含的)地址:a本来是你家,后来你搬家了,那么地址肯定改变了,所以(指针p包含)地址也改变了。由0x7ffe95265664这个原本是指向a的地址变为指向b的地址:0x7ffe95265660。
3. 在调⽤函数,重新赋值时,变量地址是否改变?
按值传递机制:给函数传递变元时,变元值不会直接传递给函数,⽽是先复制变元值的副本,存储在栈上,再使这个副本可⽤于函数,⽽不是使⽤初始值。
这个概念乍⼀看没什么难理解的。但是⾥⾯的含义也不仅仅停留在表⾯。不信?我们重点来谈谈:被调⽤函数修改属于调⽤函数的变量值的⽅式。(被调⽤函数和调⽤函数的关系就像被除数与除数的关系,不难理解吧?)
被调⽤函数修改放在调⽤函数括号内的变量值的唯⼀⽅式是:把变量的地址接收为变元值。(这句话想理解透没那么简单)
给被调⽤函数传递地址时,它只是传递地址的副本,⽽不是初始值。但是,副本仍是⼀个地址,仍引⽤最初的变量。这也是将变量地址传给scanf函数的原因,不传递地址,scanf这个被调⽤函数就没办法在最初的变量地址中存储值。还是看代码吧。
1 #include <stdio.h>
2
3void change_int(int *a);
4
5int main(void)
6  {
7int a = 2;
8      printf("a本来的地址: %p\n", &a);
9
10      change_int(&a);
11      printf("被调⽤后a = %d\n", a);
12      printf("a后来的地址: %p\n", &a);
13
14return0;
15  }
16
17void change_int(int *a)
18  {
19      printf("在被调⽤函数⾥⾯a的地址:%p\n", &a);
20      *a = 3;
21  }
22  ~
指针函数的作用输出结果:a本来存储的地址: 0x7ffc75a6f48c
在被调⽤函数⾥⾯a的地址:0x7ffc75a6f468
被调⽤后a = 3
a后来的存储地址: 0x7ffc75a6f48c
分析结果:看到没?a本来存储的地址和后来存储的地址是⼀致的,说明被调⽤函数的作⽤是:通过调⽤函数,将main函数(不⼀定是在main函数⾥哦)中a的地址内的值改变。⽽a本⾝的地址是不变的。再看,在被调⽤函数(change_int(int *a) )⾥⾯a的地址:0x7ffc75a6f468,这说明什么?说明给被调⽤函数传递地址时,它只是传递地址的副本,⽽不是初始值。剩下没说的就是:在main函数⾥,a本来等于2的,后来通过调⽤函数,在被调⽤函数⾥(的*a=3)将a变成3,这⼜说明什么?要想改变变量的值,要把变量的地址作为为变元值。
接下来要谈的是,将指针作为变元。这⾥你会知道我为什么说上⾯的⼀句话理解透没那么简单。先从最简单的⼊⼿,看代码:
1  #include <stdio.h>
2
3int main(void)
4  {
5char *a = "abc";
6      scanf("%s",a);//这句⽬的是调⽤scanf函数对a重新赋值。
7      printf("%s",a);
8return0;
9  }//这个代码的⽬的是将a中的abc变成sss
这个代码可以编译连接成功。但是当输⼊:sss 按回车时,出现了这么⼀个错误:Segmentation fault (核⼼已转储),这个错误是由于地址出错。
那么疑问来了,a是⼀个地址吧?那么我给被调⽤函数传递的是a地址啊,为什么不能成功改变a存储的值(即abs)呢?先看看代码
1 #include <stdio.h>
2
3void change_piont(char **a);//⽤于改变指针所存储的值的函数。
4void change_int(int *b);//⽤于改变变量的值的函数。
5void change_array(char *c, int n);
6
7int main(void)
8  {
9char *a = "absdefg";
10      printf("a = %s\n", a); //输出absdefg
11      change_piont(&a);//将指针变量a的地址作为变元
12      printf("%s\n", a);//输出ddd,即改变指针a存储的值成功。
13
14int b = 2;
15      printf("origin = %p\n", &b);//b原来的地址:xxx
16
17      change_int(&b);//将变量b的地址作为变元。
18      printf("(later)b = %d\n",b);//调⽤后b的值,输出为3
19      printf("later = %p\n", &b);//经过调⽤⼀回后,b的地址:还是xxx
20
21
22char c[5] = "abcd";//数组c本来的值:abcd
23      change_array(c,5);
24      printf("c = %s\n",c);//输出结果:abcf
25
26return0;
27  }
28
29void change_piont(char **a)
30  {
31char *b = "ddd";
32      *a = b;
33// printf("%s\n", *a);
34  }
35
36void change_int(int *b)
37  {
38      printf("chang_int -> the address of b = %p\n", &b);//这个地址和原本b的地址不⼀样,说明它是⼀个副本。
39      *b = 3;
40  }
41
42void change_array(char *c, int n)
43  {
44      c[3] = 'f';
45  }
46/******************************************************
47  * 原本输出结果:
48  * a = absdefg
49  * ddd
50  * origin = 0x7ffc5c289f84
51  * chang_int -> the address of b = 0x7ffc5c289f58
52  * (later)b = 3
53  * later = 0x7ffc5c289f84
54  * c = abcf
55  * ****************************************************/
⼤多数都可以在注释上看明⽩,这⾥我重点要说这句(11句):change_piont(&a);//将指针变量a的地
址作为变元。还记得上⾯的疑问吧?a是⼀个地址吧?那么我给被调⽤函数传递的是a地址啊,为什么不能成功改变a存储的值(即abs)。注意这⾥调⽤函数括号内与上⼀个代码中(scanf("%s",a))括号内的a有什么不同,多了⼀个取址符:&
这⾥要注意了:指针a虽然是⼀个地址,但指针a也是⼀个变量。上⾯概念说了,改变⼀个变量所存储的值唯⼀的变法就是将它的地址作为变元。指针变量的地址是什么?是:&a。还是在看⼀次代码吧。
1 #include <stdio.h>
2
3void change_piont(char **a);
4
5int main(void)
6  {
7char *a = "abc";
8      printf("a本来的地址:%p\n", &a);
9      printf("a本来存储的地址:%p\n", a);
10
11      change_piont(&a);
12      printf("调⽤后a的地址:%p\n",&a);
13      printf("调⽤后a存储的地址:%p\n",a);
14
15      printf("a = %s\n",a);
16return0;
17  }
18
19void change_piont(char **a)
20  {
21      printf("在被调⽤函数内a的地址: %p\n",&a);
22      *a = "sss";
23
24  }
25/************************************************
26  * result:
27  * a本来的地址:0x7ffe6bbd2088
28  * a本来存储的地址:0x400680
29  * 在被调⽤函数内a的地址: 0x7ffe6bbd2068
30  * 调⽤后a的地址:0x7ffe6bbd2088
31  * 调⽤后a存储的地址:0x400725
32  * a = sss
33  * **********************************************/
看结果已经很清楚了。a本来的地址就是指针a本⾝的地址。a本来存储的地址就是本来存储在a指针的地址。举个例⼦吧。char *a = &b。a本来的地址就是:&a。a本来存储的地址就是:&b 或者 a。
4. 结论:(1)在直接对变量重新赋值时,变量的地址不变。变的是存储在这个地址⾥的值。
(2)对于指针,有两种可能。⼀是通过指针对指针所指向的地址重新赋值。这种情况下指针指向的地址不变,变的是指针指向的地址⾥⾯的值。⼆是直接对指针变量重新赋值,这种情况指针指向的地址会改变。(看看第⼆点的代码就清楚了)(3)在调⽤函数时,将(指针)变量的地址做为变元传递,并不是直接将这个变量原本的地址传递给函数调⽤,⽽是通过⼀个地址的副本,在改变变量的值时,变量存储的地址没有改变。特别要注意指针的地址,它是⼀个char**型。指针本来就是char*型。它的地址是char**型应该没什么疑问吧?

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。