博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详细易懂的解释变量和指针变量重新赋值的过程和问题
阅读量:6151 次
发布时间:2019-06-21

本文共 6230 字,大约阅读时间需要 20 分钟。

在研究按值传递机制时,发现一些模糊的概念。就是在对一个原本的(指针)变量重新给定另外一个值时,会不会改变这个变量原本所在的内存位置(即地址)。因此,决定深入研究一下。而且这也是必要的。

    1. 给一个变量重新赋值时,地址的变化
      1 //验证变量在被赋值(以及被重赋值)时原本分配的内存地址是否会改变。 2   3  #include 
      4 5 int main(void) 6 { 7 int a;//声明a,即已经给a分配一个内存地址 8 printf("声明a时的地址:%p\n", &a); 9 10 a = 1; //初始化a11 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 17 return 0;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 
      3 4 int main(void) 5 { 6 int *p = NULL; 7 int 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 19 int b = 2;20 p = &b; 21 printf("p指针存储的b地址:%p\n", p); 22 printf("p指针的地址:%p\n", &p);23 24 return 0;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 
      2 3 void change_int(int *a); 4 5 int main(void) 6 { 7 int 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 14 return 0;15 }16 17 void 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 
      2 3 int main(void)4 {5 char *a = "abc";6 scanf("%s",a);//这句目的是调用scanf函数对a重新赋值。7 printf("%s",a);8 return 0;9 }//这个代码的目的是将a中的abc变成sss

       

      这个代码可以编译连接成功。但是当输入:sss 按回车时,出现了这么一个错误:Segmentation fault (核心已转储),这个错误是由于地址出错。
      那么疑问来了,a是一个地址吧?那么我给被调用函数传递的是a地址啊,为什么不能成功改变a存储的值(即abs)呢?先看看代码
      1 #include 
      2 3 void change_piont(char **a);//用于改变指针所存储的值的函数。 4 void change_int(int *b);//用于改变变量的值的函数。 5 void change_array(char *c, int n); 6 7 int main(void) 8 { 9 char *a = "absdefg";10 printf("a = %s\n", a); //输出absdefg11 change_piont(&a);//将指针变量a的地址作为变元12 printf("%s\n", a);//输出ddd,即改变指针a存储的值成功。13 14 int b = 2;15 printf("origin = %p\n", &b);//b原来的地址:xxx16 17 change_int(&b);//将变量b的地址作为变元。18 printf("(later)b = %d\n",b);//调用后b的值,输出为319 printf("later = %p\n", &b);//经过调用一回后,b的地址:还是xxx20 21 22 char c[5] = "abcd";//数组c本来的值:abcd23 change_array(c,5);24 printf("c = %s\n",c);//输出结果:abcf25 26 return 0;27 }28 29 void change_piont(char **a)30 {31 char *b = "ddd";32 *a = b;33 // printf("%s\n", *a);34 }35 36 void change_int(int *b)37 {38 printf("chang_int -> the address of b = %p\n", &b);//这个地址和原本b的地址不一样,说明它是一个副本。39 *b = 3;40 }41 42 void change_array(char *c, int n)43 {44 c[3] = 'f';45 }46 /******************************************************47 * 原本输出结果:48 * a = absdefg49 * ddd50 * origin = 0x7ffc5c289f8451 * chang_int -> the address of b = 0x7ffc5c289f5852 * (later)b = 353 * later = 0x7ffc5c289f8454 * c = abcf55 * ****************************************************/

       

      大多数都可以在注释上看明白,这里我重点要说这句(11句):change_piont(&a);//将指针变量a的地址作为变元。还记得上面的疑问吧?a是一个地址吧?那么我给被调用函数传递的是a地址啊,为什么不能成功改变a存储的值(即abs)。注意这里调用函数括号内与上一个代码中(scanf("%s",a))括号内的a有什么不同,多了一个取址符:&
      这里要注意了:指针a虽然是一个地址,但指针a也是一个变量。上面概念说了,改变一个变量所存储的值唯一的变法就是将它的地址作为变元。指针变量的地址是什么?是:&a。还是在看一次代码吧。
      1 #include 
      2 3 void change_piont(char **a); 4 5 int main(void) 6 { 7 char *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);16 return 0;17 }18 19 void change_piont(char **a)20 {21 printf("在被调用函数内a的地址: %p\n",&a);22 *a = "sss";23 24 }25 /************************************************26 * result:27 * a本来的地址:0x7ffe6bbd208828 * a本来存储的地址:0x40068029 * 在被调用函数内a的地址: 0x7ffe6bbd206830 * 调用后a的地址:0x7ffe6bbd208831 * 调用后a存储的地址:0x40072532 * a = sss33 * **********************************************/

       

      看结果已经很清楚了。a本来的地址就是指针a本身的地址。a本来存储的地址就是本来存储在a指针的地址。举个例子吧。char *a = &b。a本来的地址就是:&a。a本来存储的地址就是:&b 或者 a。
    4. 结论:(1)在直接对变量重新赋值时,变量的地址不变。变的是存储在这个地址里的值。
                  (2)对于指针,有两种可能。一是通过指针对指针所指向的地址重新赋值。这种情况下指针指向的地址不变,变的是指针指向的地址里面的值。二是直接对指针变量重新赋值,这种情况指针指向的地址会改变。(看看第二点的代码就清楚了)
                  (3)在调用函数时,将(指针)变量的地址做为变元传递,并不是直接将这个变量原本的地址传递给函数调用,而是通过一个地址的副本,在改变变量的值时,变量存储的地址没有改变。特别要注意指针的地址,它是一个char**型。指针本来就是char*型。它的地址是char**型应该没什么疑问吧?
    5. 转载:https://www.cnblogs.com/busui/p/5690789.html

转载于:https://www.cnblogs.com/i6010/articles/7921635.html

你可能感兴趣的文章
onInterceptTouchEvent和onTouchEvent调用时序
查看>>
android防止内存溢出浅析
查看>>
4.3.3版本之引擎bug
查看>>
SQL Server表分区详解
查看>>
使用FMDB最新v2.3版本教程
查看>>
SSIS从理论到实战,再到应用(3)----SSIS包的变量,约束,常用容器
查看>>
STM32启动过程--启动文件--分析
查看>>
垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
查看>>
淘宝的几个架构图
查看>>
Android扩展 - 拍照篇(Camera)
查看>>
数据加密插件
查看>>
linux后台运行程序
查看>>
win7 vs2012/2013 编译boost 1.55
查看>>
IIS7如何显示详细错误信息
查看>>
Tar打包、压缩与解压缩到指定目录的方法
查看>>
配置spring上下文
查看>>
Python异步IO --- 轻松管理10k+并发连接
查看>>
Oracle中drop user和drop user cascade的区别
查看>>
登记申请汇总
查看>>
Office WORD如何取消开始工作右侧栏
查看>>