zoukankan      html  css  js  c++  java
  • C++之指针使用

    C++指针使用的好坏直接反映了编程人员水平的高低,下面从指针和数组的区别、指针参数是如何传递内存、野指针、malloc/free、new/delete和内存耗尽怎么办方面进行总结。

    一 指针和数组对比

      C++/C程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。

    (1)修改内容

    char a[] = “hello”;
    a[0] = ‘X’;  // 数组可以修改字符串内容
    
    char *p = “world”; // 注意p指向常量字符串
    // 编译器不能发现该错误
    // 但该语句企图修改常量字符串的内容而导致运行出错
    p[0] = ‘X’; 

    (2)内容复制和比较

    // 数组…
    char a[] = "hello";
    char b[10];
    strcpy(b, a); // 不能用 b = a;
    if(strcmp(b, a) == 0) // 不能用 if (b == a)
    
    // 指针…
    int len = strlen(a);
    char *p = (char *)malloc(sizeof(char)*(len+1));
    strcpy(p,a); // 不要用 p = a;
    if(strcmp(p, a) == 0) // 不要用 if (p == a)

    (3)计算内存容量

    char a[] = "hello world";
    char *p = a;
    cout<< sizeof(a) << endl; // 12字节
    cout<< sizeof(p) << endl; // 4字节

    注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针

    void Func(char a[100])
    {
     cout<< sizeof(a) << endl; // 4字节而不是100字节
    }

    二 指针参数如何传递内存

    (1)错误示例

    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"); // 运行错误
    }

      编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。

      在上面的例子中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。

    (2)解决方法1:使用指向指针的指针

    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);
    }

    (3)解决方法2:指针作为函数返回值

    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);
    }
    注:(1)在上面的例子中,要特别注意在函数调用完后用free释放malloc的内存;
      (2)不要在函数体内返回栈内存的指针

    三 野指针

      “野指针”不是NULL指针,是指向“垃圾”内存的指针。

      人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。 

      “野指针”的成因主要有三种:

      (1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。

      (2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。

      (3)指针操作超越了变量的作用域范围。

    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是“野指针”
    }

    四 malloc/free/new/delete

      malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

      对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

      因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

    五 内存耗尽怎么办

      如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。

      (1)判断指针是否为NULL,如果是则马上用return语句终止本函数。

      (2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。

      (3)用_set_new_hander函数为new设置用户自己定义的异常处理函数。

  • 相关阅读:
    js自动小轮播
    js字符串
    工资
    可是姑娘,你为什么要编程呢?
    程序猿媳妇儿注意事项
    js勾选时显示相应内容
    js点击显示隐藏
    js选项卡
    js数组
    js旋转V字俄罗斯方块
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/8879968.html
Copyright © 2011-2022 走看看