指针作用域

<script type="text/javascript">var username = "xuzewei_2"; var _blogger = username; var blog_address = "http://blog.csdn.net/thebestdavid"; var static_host = "http://static.blog.csdn.net"; var currentUserName = "WY1468840047";</script>
<script id="popuLayer_js_q" src="http://ads.csdn.net/js/popuLayer.js" defer="defer" type="text/javascript"></script>
<script src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
关闭
width="300" height="250" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" οnlοad="var i=this.id,s=window.google_iframe_oncopy,H=s&&s.handlers,h=H&&H[i],w=this.contentWindow,d;try{d=w.document}catch(e){}if(h&&d&&(!d.body||!d.body.firstChild)){if(h.call){setTimeout(h,0)}else if(h.match){try{h=s.upd(h,i)}catch(e){}w.location.replace(h)}}" id="aswift_0" name="aswift_0" style="left:0;position:absolute;top:0;">
<script>(adsbygoogle=window.adsbygoogle || []).push({});</script><script>document.getElementById('popuLayer_js_q').οnlοad=function(){ var styObjd=styObj={width:'302px',height:'275px'};window.CSDN.Layer.PopuLayer('#layerd_google',{storageName:'layerd',styleObj:styObjd,total:50,expoire:1000*60}); }</script>
href="http://static.blog.csdn.net/css/comment1.css" type="text/css" rel="stylesheet" /> href="http://static.blog.csdn.net/css/style1.css" type="text/css" rel="stylesheet" /><script language="JavaScript" type="text/javascript" src="http://download.csdn.net/js/jquery.cookie.js"></script><script type="text/javascript" src="http://c.csdnimg.cn/rabbit/search-service/main.js"></script> rel="stylesheet" href="http://static.blog.csdn.net/public/res-min/markdown_views.css?v=1.0" /> rel="stylesheet" href="http://static.blog.csdn.net/css/category.css?v=1.0" /><script type="text/javascript" src="http://static.blog.csdn.net/public/res/bower-libs/MathJax/MathJax.js?config=TeX-AMS_HTML"></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/web-storage-cache.min.js"></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/replace.min.js"></script> <script type="text/ecmascript">window.quickReplyflag = true; var isBole = false; var fasrc="http://my.csdn.net/my/favorite/miniadd?t=C%2b%2b+%e6%8c%87%e9%92%88%e4%b8%8e%e4%bd%9c%e7%94%a8%e5%9f%9f&u=http://blog.csdn.net/thebestdavid/article/details/23165597"</script>

C++ 指针与作用域

标签: 指针作用域
1137人阅读 评论(0) 收藏 举报
<script type="text/javascript">$(function () { try { var lib = eval("("+$("#lib").attr("value")+")"); var html = ""; if (lib.err == 0) { $.each(lib.data, function (i) { var obj = lib.data[i]; //html += ' ' + obj.name + "  "; html += '
分类:
作者同类文章 X
    <script type="text/javascript" src="http://static.blog.csdn.net/scripts/category.js"></script>

    C/C++中的指针操作是一个令人抓狂的问题,这几天在温习林锐的《高质量C++C编程指南》,里面的内存管理这一章对我受益匪浅。看到下面的一段内容,不禁和作者提出相同的疑问:该程序不出错是因为编译器的原因吗?并在网上查找相关资料。


    源码1:

    1. #include <iostream>  
    2. using namespace std;  
    3.   
    4. class A  
    5. {  
    6. public:  
    7.     void Func(void)  
    8.     {  
    9.         cout<<"Func of class A"<<endl;  
    10.     }  
    11. };  
    12.   
    13. void Test(void)  
    14. {  
    15.     A *p;  
    16.     {  
    17.         A a;  
    18.         p = &a; // 注意 a 的生命期  
    19.     }  
    20.     p->Func(); // p是“野指针”  
    21. }  
    22.   
    23. int main()  
    24. {  
    25.     Test();  
    26.     return 0;  
    27. }  
    #include <iostream>
    using namespace std;
    
    class A
    {
    public:
        void Func(void)
        {
            cout<<"Func of class A"<<endl;
        }
    };
    
    void Test(void)
    {
        A *p;
        {
            A a;
            p = &a; // 注意 a 的生命期
        }
        p->Func(); // p是“野指针”
    }
    
    int main()
    {
        Test();
        return 0;
    }

    为了探索这个问题,我分别在CodeBlocks、VC++6.0和VS2010上运行这段代码,发现并无错误,所以,这个并非编译器的原因,那么到底是什么原因呢?

    1、我们知道在Test函数中内部用花括号括起来的部分是一个局部作用范围,因此,该语句块拥有局部的作用域,在里面定义的局部对象a,是一个存放在栈中的自动变量,一旦离开了这个作用域(离开花括号),就不复存在了。可以通过简单的单步调试,跟踪对象a的变化,实践证明,这个想法是对的。

    2、一般来说,一个对象所占的空间大小只取决于该对象中数据成员所占的空间,而与成员函数无关。通过下面一段代码可以验证

    源码2:

    1. #include <iostream>  
    2. using namespace std;  
    3.   
    4. class A  
    5. {  
    6.     int i;  
    7.     char chr;  
    8. };  
    9. class B  
    10. {  
    11.     int i;  
    12.     char chr;  
    13.     void fun()  
    14.     {  
    15.         int j = 1;  
    16.         cout<<j<<endl;  
    17.     }  
    18. };  
    19. int main()  
    20. {  
    21.     A a;  
    22.     B b;  
    23.     cout<<"sizeof A = "<<sizeof(a)<<endl;  
    24.     cout<<"sizeof B = "<<sizeof(b)<<endl;  
    25.     return 0;  
    26. }  
    #include <iostream>
    using namespace std;
    
    class A
    {
        int i;
        char chr;
    };
    class B
    {
        int i;
        char chr;
        void fun()
        {
            int j = 1;
            cout<<j<<endl;
        }
    };
    int main()
    {
        A a;
        B b;
        cout<<"sizeof A = "<<sizeof(a)<<endl;
        cout<<"sizeof B = "<<sizeof(b)<<endl;
        return 0;
    }

    输出为:

    ,在类A和类B中都有一个int和char类型的成员变量,根据内存对齐,取其中较大的int变量的字节数4作为倍数,所以sizeof(A) = 2 * 4 = 8字节。尽管类B中有成员函数fun,但是该对象b所占用空间并没有计算fun函数的空间。

    3、那么成员函数放在哪里?对象是怎么调用成员函数的呢?

    先来看一个图片:



    通过图片可以看出,对象实例的成员函数(非静态成员函数)有一个公用函数代码存储空间,对象通过this指针调用公用函数代码中的成员函数,这样可以减少存储空间的使用。

    小结:1) 不论成员函数在类内定义还是在类外定义,成员函数的代码段都用同一种方式存储;

        2) 函数代码是存储在对象空间之外的;

        3) 通常所说的“某某对象的成员函数”,是从逻辑的角度而言的;而成员函数的存储方式,是从物理的角度而言的,二者是不矛盾的。

    4、返回原来讨论的问题,既然成员函数存放在公用函数代码段里面,而在源码1中,指针p 是保存了在花括号里面的对象a的地址的,对象a销毁了,我们不能使用变量a,但是它的地址是不会改变的,编译器只是销毁了对象a这个变量,并没有销毁对象a原来所在存储区域(当然是在栈里面的存储区域)中的成员变量和成员函数,所以,这里得到的指针p,还是指向花括号中定义的对象a的首地址,这就是ptr就是原来的对象a的this指针,当使用p->Func();语句调用成员函数的时候,自然就可以通过this指针在公用函数代码段中找到对应的成员函数,执行该函数(我猜想在公用函数代码段中应该维护了一个this指针与成员函数的对照表)。

    5、错误的调用返回了正确的结果,着实令人头痛。显然,应该坚决杜绝这样的调用方法。对于野指针(不是NULL指针,是指向被释放的或者访问受限内存的指针),我们使用delete或者free释放存储空间的时候,把指针p赋值为NULL是一个好习惯,这样,通过if(p != NULL)语句就可以判断该指针是否是合法的。


    扩展:

    上述研究的内容是针对一个对象实例的,假设是一个int类型或者字符串类型,结果是否一样呢。来看一段代码。

    源码3:

    1. #include <iostream>  
    2. using namespace std;  
    3.   
    4. char *getstr(int flag)  
    5. {  
    6.     if(flag == 1)  
    7.     {  
    8.         char *str = "12345";//字符串"12345"存放在文字常量区  
    9.         return str;  
    10.     }  
    11.     else  
    12.     {  
    13.         char str[] = "ABCDE";//字符数组  
    14.         return str;  
    15.     }  
    16. }  
    17.   
    18. int *getint()  
    19. {  
    20.     int i = 2014;  
    21.     return &i;  
    22. }  
    23.   
    24. int main()  
    25. {  
    26.     //字符串类型测试  
    27.     cout<<"字符串类型测试(字符串):"<<getstr(1)<<endl;  
    28.     cout<<"字符串类型测试(字符数组):"<<getstr(2)<<endl;  
    29.     //char *str = getstr(2);//使用指针str保存返回的getstr(2)指针,打印出来的是乱码,说明指针没有正确返回  
    30.     //使用getstr(2)打印确实正常的,如下:  
    31.     for(int i = 0; i < 6; i++)  
    32.     {  
    33.         cout<<"str"<<i<<": "<<getstr(2)[i]<<endl;  
    34.     }  
    35.       
    36.   
    37.     cout<<endl<<"****************分割线******************"<<endl<<endl;  
    38.     //整型类型测试  
    39.     cout<<"整型类型测试:"<<*getint()<<endl;  
    40.     return 0;  
    41. }  
    #include <iostream>
    using namespace std;
    
    char *getstr(int flag)
    {
        if(flag == 1)
        {
            char *str = "12345";//字符串"12345"存放在文字常量区
            return str;
        }
        else
        {
            char str[] = "ABCDE";//字符数组
            return str;
        }
    }
    
    int *getint()
    {
        int i = 2014;
        return &i;
    }
    
    int main()
    {
        //字符串类型测试
        cout<<"字符串类型测试(字符串):"<<getstr(1)<<endl;
        cout<<"字符串类型测试(字符数组):"<<getstr(2)<<endl;
    	//char *str = getstr(2);//使用指针str保存返回的getstr(2)指针,打印出来的是乱码,说明指针没有正确返回
    	//使用getstr(2)打印确实正常的,如下:
    	for(int i = 0; i < 6; i++)
    	{
    		cout<<"str"<<i<<": "<<getstr(2)[i]<<endl;
    	}
    	
    
        cout<<endl<<"****************分割线******************"<<endl<<endl;
        //整型类型测试
        cout<<"整型类型测试:"<<*getint()<<endl;
        return 0;
    }


    运行结果(VC++6.0):


    1) 对于整型变量来说,与之前的说法是一致的,getint方法中变量i在退出函数时便不能使用了,但是返回的地址所指向的存储区域内容并没有改变依旧是2014;

    2) char *str = "12345";这种定义方式的字符串"12345"是存放在文字常量区的,该字符串不能修改,其生命周期和整个程序的生命周期一样长,但是依旧不建议这么编写代码。

    3) char str[] = "ABCDE";这里定义的是一个字符数组,这是一种直观的初始化方式,注意这样不代表字符串"ABCDE"是存放在文字常量区,而是等价于char str[] = {'A', 'B', 'C', 'D', 'E', '\0'};;离开了getstr函数,字符数组str便不复存在,这里返回的指针不一定是原先字符串"ABCDE"的首地址,这里打印出来的地址是乱码,但是单个取值却没有问题。个人猜想:数组名和指针不是一个概念,getstr函数中分配字符数组空间的时候,还要存储数据的类型等信息,返回的也是数组名str,然后离开了getstr函数,数组str不存在了,返回的“数组名”是没有意义的,因此打印出来的是乱码。


    总结:

    离开了作用域的变量,变量被销毁,变量所在存储区域的内容并没有被系统自动实时释放和回收;也许该存储区域只是做了“可分配”标记。


    注:不能因为系统没有实时回收这些变量而存在侥幸心理,熟练使用指针并对内存管理有清晰的理解才是王道。


    C/C++中的内存管理学问太深,这篇博客是小弟个人拙见,也许漏洞百出,还是希望各路高手指教,不胜感激!


    本文版权归作者和CSDN所有,欢迎转载,转载请注明出处:http://blog.csdn.net/thebestdavid/article/details/23165597

    <script>window._bd_share_config = { "common": { "bdSnsKey": {}, "bdText": "", "bdMini": "1", "bdMiniList": false, "bdPic": "", "bdStyle": "0", "bdSize": "16" }, "share": {} }; with (document) 0[(getElementsByTagName('head')[0] || body).appendChild(createElement('script')).src = 'http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion=' + ~(-new Date() / 36e5)];</script> rel="stylesheet" href="http://static.blog.csdn.net/css/blog_detail.css" /> <script type="text/javascript" id="bdshare_js" data="type=tools&amp;uid=1536434" src="http://bdimg.share.baidu.com/static/js/bds_s_v2.js?cdnversion=411915"></script><script type="text/javascript">document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000)</script>
    0
    0
     
     
    <script type="text/javascript">function btndigga() { $(".tracking-ad[data-mod='popu_222'] a").click(); } function btnburya() { $(".tracking-ad[data-mod='popu_223'] a").click(); }</script>
    我的同类文章
    <script type="text/javascript">$(function () { GetCategoryArticles('1593807', 'xuzewei_2','foot','23165597'); });</script>
    <script src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> width="728" height="90" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" οnlοad="var i=this.id,s=window.google_iframe_oncopy,H=s&&s.handlers,h=H&&H[i],w=this.contentWindow,d;try{d=w.document}catch(e){}if(h&&d&&(!d.body||!d.body.firstChild)){if(h.call){setTimeout(h,0)}else if(h.match){try{h=s.upd(h,i)}catch(e){}w.location.replace(h)}}" id="aswift_1" name="aswift_1" style="left:0;position:absolute;top:0;"><script>(adsbygoogle=window.adsbygoogle || []).push({});</script>
    <script language="javascript" type="text/javascript">$(function(){ $.get("/xuzewei_2/svc/GetSuggestContent/23165597",function(data){ $("#suggest").html(data); }); });</script> rel="stylesheet" href="http://static.blog.csdn.net/css/replace.css" />

    参考知识库

    img
    .NET知识库

    img
    C++知识库

    img
    软件测试知识库

    猜你在找
    <script src="http://csdnimg.cn/jobreco/job_reco.js" type="text/javascript"></script> <script type="text/javascript">csdn.position.showEdu({ sourceType: "blog", searchType: "detail", searchKey: "23165597", username: "WY1468840047", recordcount: "5", containerId: "adCollege" //容器DIV的id。 });</script>
    <script type="text/javascript">$(function () { setTimeout(function () { var searchtitletags = 'C++ 指针与作用域' + ',' + $("#tags").html(); searchService({ index: 'blog', query: searchtitletags, from: 5, size: 5, appendTo: '#res', url: 'recommend', his: 2, client: "blog_cf_enhance", tmpl: '
    #{ title }
    ' }); }, 500); });</script>
    <script src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> width="728" height="90" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" οnlοad="var i=this.id,s=window.google_iframe_oncopy,H=s&&s.handlers,h=H&&H[i],w=this.contentWindow,d;try{d=w.document}catch(e){}if(h&&d&&(!d.body||!d.body.firstChild)){if(h.call){setTimeout(h,0)}else if(h.match){try{h=s.upd(h,i)}catch(e){}w.location.replace(h)}}" id="aswift_2" name="aswift_2" style="left:0;position:absolute;top:0;"><script>(adsbygoogle=window.adsbygoogle || []).push({});</script>
    查看评论

      暂无评论

    发表评论
    • 用 户 名:
    • WY1468840047
    •   
    * 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    • 个人资料
      • 访问:89461次
      • 积分:1122
      • 等级:
        积分:1122
      • 排名:千里之外
      • 原创:23篇
      • 转载:0篇
      • 译文:0篇
      • 评论:246条
    • 文章分类
    • 最新评论
    您有 118条新通知
    收藏助手

    提问

    您的问题将会被发布在“技术问答”频道 ×
    该问题已存在,请勿重复提问
    插入图片
    | | | | | |
      
     
     
    0 0 0:0
    推荐标签:
    我要悬赏
    取消 发布
    可能存在类似的问题:

    保存代码片

    整理和分享保存的代码片,请访问代码笔记
    • *标题
    • *描述
    •  标签
      指针x 作用域x
    0个广告已降服
    可以清爽的上网了~
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值