zoukankan      html  css  js  c++  java
  • C++ 内存。二重指针,指针的高级使用

    内存分配方式有三种:
    (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的
    整个运行期间都存在。例如全局变量,static 变量。
    (2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
    数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集
    中,效率很高,但是分配的内存容量有限。
    (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多
    少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期
    由我们决定,使用非常灵活,但问题也最多。

    ?? 内存分配未成功,却使用了它。
    编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,
    在使用内存之前检查指针是否为NULL。如果指针p 是函数的参数,那么在函数的入口
    处用assert(p!=NULL)进行检查。如果是用malloc 或new 来申请内存,应该用if(p==NULL)
    或if(p!=NULL)进行防错处理。

    ?? 内存分配虽然成功,但是尚未初始化就引用它。

    ?? 内存分配成功并且已经初始化,但操作越过了内存的边界。

    ?? 忘记了释放内存,造成内存泄露。
    含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你
    看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
    动态内存的申请与释放必须配对,程序中malloc 与free 的使用次数一定要相同,否
    则肯定有错误(new/delete 同理)。

    ?? 释放了内存却继续使用它。
    有三种情况:
    (1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内
    存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
    (2)函数的return 语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,
    因为该内存在函数体结束时被自动销毁。
    (3)使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”


    指针参数是如何传递内存的?
    如果函数的参数是一个指针,不要指望用该指针去申请动态内存


    Test 函数的语句GetMemory(str, 200)并没有使str 获得期望的内存,str 依旧是NULL,
    为什么?

    void GetMemory(char *p, int num)
    {
    p = (char *)malloc(sizeof(char) * num);
    }
    void Test(void)
    {
    char *str = NULL;
    GetMemory(str, 100); // str 仍然为 NULL
    strcpy(str, "hello"); // 运行错误
    }

    毛病出在函数GetMemory 中。编译器总是要为函数的每个参数制作临时副本,指针
    参数p 的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p 的内容,就导致
    参数p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请
    了新的内存,只是把_p 所指的内存地址改变了,但是p 丝毫未变。所以函数GetMemory
    并不能输出任何东西。事实上,每执行一次GetMemory 就会泄露一块内存,因为没有用
    free 释放内存。
    如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”
    void GetMemory2(char **p, int num)
    {
    *p = (char *)malloc(sizeof(char) * num);
    }
    void Test2(void)
    {
    char *str = NULL;
    GetMemory2(&str, 100); // 注意参数是 &str,而不是str
    strcpy(str, "hello");
    cout<< str << endl;
    free(str);
    }

    由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态
    内存。这种方法更加简单
    char *GetMemory3(int num)
    {
    char *p = (char *)malloc(sizeof(char) * num);
    return p;
    }
    void Test3(void)
    {
    char *str = NULL;
    str = GetMemory3(100);
    strcpy(str, "hello");
    cout<< str << endl;
    free(str);
    }
    用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return 语句用错
    了。这里强调不要用return 语句返回指向“栈内存”的指针,因为该内存在函数结束时
    自动消亡
    char *GetString(void)
    {
    char p[] = "hello world";
    return p; // 编译器将提出警告
    }
    void Test4(void)
    {
    char *str = NULL;
    str = GetString(); // str 的内容是垃圾
    cout<< str << endl;
    }
    用调试器逐步跟踪Test4,发现执行str = GetString 语句后str 不再是NULL 指针,
    但是str 的内容不是“hello world”而是垃圾。
    char *GetString2(void)
    {
    char *p = "hello world";
    return p;
    }
    void Test5(void)
    {
    char *str = NULL;
    str = GetString2();
    cout<< str << endl;
    }
    函数Test5 运行虽然不会出错,但是函数GetString2 的设计概念却是错误的。因为
    GetString2 内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内
    恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

    原文:http://www.cppblog.com/mzty/archive/2005/11/09/1004.html

  • 相关阅读:
    SAP S/4HANA extensibility扩展原理介绍
    SAP CRM系统订单模型的设计与实现
    使用nodejs代码在SAP C4C里创建Individual customer
    SAP Cloud for Customer Account和individual customer的区别
    Let the Balloon Rise map一个数组
    How Many Tables 简单并查集
    Heap Operations 优先队列
    Arpa’s obvious problem and Mehrdad’s terrible solution 思维
    Passing the Message 单调栈两次
    The Suspects 并查集
  • 原文地址:https://www.cnblogs.com/linlf03/p/2255158.html
Copyright © 2011-2022 走看看