zoukankan      html  css  js  c++  java
  • c++数组

    在复习到c++的时候,发现习惯了java,python直接对数据的调用或者new之后对于c++的数组运用的不太习惯,在此整理下

    1.1基础概念


    C++从右往左赋值
    C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合
    在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:

    type arrayName [ arraySize ];
    

    初始化二维数组
    多维数组可以通过在括号内为每行指定值来进行初始化。下面是一个带有 3 行 4 列的数组。

    int a[3][4] = {  
     {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
     {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
     {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
    };
    内部嵌套的括号是可选的,下面的初始化与上面是等同的:
    
    int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
    

    数组名是指向数组中第一个元素的常量指针。因此,在下面的声明中:

    double balance[50];
    

    balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。因此,下面的程序片段把 p 赋值为 balance 的第一个元素的地址:

    double *p;
    double balance[10];
    
    p = balance;
    使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。
    

    形式参数是一个指针:

    void myFunction(int *param)
    {
    .
    .
    .
    }
    

    C++ 不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。

    如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数,如下:

    int * myFunction()
    {
    .
    .
    .
    }
    如
    // 要生成和返回随机数的函数
    int * getRandom( )
    {
      static int  r[10];
     
      // 设置种子
      srand( (unsigned)time( NULL ) );
      for (int i = 0; i < 10; ++i)
      {
        r[i] = rand();
        cout << r[i] << endl;
      }
     
      return r;
    }
    

    1.2数组进阶

    在C++中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完成:

    指针变量名=new 类型名(初始化式);
    delete 指针名;

    例如:1、 int *pi=new int(0);
    
          它与下列代码序列大体等价:
    
          2、int ival=0, *pi=&ival;
    

    区别:pi所指向的变量是由库操作符new()分配的,位于程序的堆区中,并且该对象未命名

    这样也可以创建一个简单的类似动态空间,实现动态一维数组
    如下

    	cin >> len;
    	int *p = new int[len];
    	cout << "请输入"<<endl;
    	for (int i = 0; i < len; ++i)
    	{
    		cin >> a;
    		p[i] = a;
    	}
        cout << "数组为:"<<p<<endl;
        delete[] p;
    	p = NULL;
    

    是不是以为就这么简单就完了?


    当然不是,那么我们就开始研究c++的动态内存分配

    1.2.1为啥需要动态分配内存

    数组是我们常用的一种数据结构.但它有一个缺点,就是用的时候必须确定数组大小.如果我们要用数组来保存的数据不确定可咋整啊?把数组定得太大浪费空间,太小的话又装不下.这是一种情况.另外就是对象太大.我们使用的数据大部分时候都默认保存在栈(stack)里面,由系统去管理,会自动给分配内存,自动给删除掉.但是stack很小,就那么几M,如果你读取一个几十M的文本内容然后保存到一个字符串里,stack肯定会被撑爆了.

    上面说的是两种最常见的两种情景.另外如果你想准确的控制内存的释放.比如内存比较紧缺,你用完一块内存后就想立马释放掉.如果系统自动去释放的话可能得等到变量生命周期结束时再释放.不会做得到立马释放.

    基于上面等一些原因于是出现了堆(heap),这是由用户自己控制的一片内存区,比stack大多了.你可以自由的在里面申请空间释放空间.

    C语言中的动态内存分配

    C语言是比较接近底层的,用它举例说动态内存的分配更容易理解.C++是作了一定程度的封装.就以数组来举例吧.假如你不知道要使用的数据具体是多少,只要运行的时候才知道.就用N表示需要N个int类型的空间.于是我们需要动态的分配一块内存,并用一个int型指针指向内存的首地址.

    int n = 123;    //这里随便赋个值,实际使用时可能是传个参数确定它的值
    
    int * p = (int*)  malloc(sizeof(int)*n);
    
    /*malloc是一个库函数,调用它去申请内存空间,它的返回值是void*指针,所以需要做下类型转换变成int*指针.它的参数是内存大小,以字节为单位,表示要申请多少个字节.int类型可能在不同的系统里占用的字节不一样,所以用sizeof计算下先. 假如你要给结构体分配内存的话,假如有结构体struct  test则是,struct test * pTest = (struct test*) malloc(sizeof(struct test));*/
    
    现在可以把弄来的内存当数组用了.实际上也不是真的数组,只是模拟拉.
    
    int * pArray = p;           //用另一个指针来指向p,因为p要保留着内存的首地址,这样后面释放内存的时候才会正确释放
    
    *pArray = 123;
    
    *(pArray + 1) = 456;
    
    free(p);      //用完了就可以这样来释放内存
    
    p = 0;       //让指针指向一块空内存,这样的好处有,比如你不小心在哪又再free下p就会出错,但让p指向空内存后多次重复free也不会出错.
    
    //free(p)不是释放p指针的内存,而是以它为首地址后面的一大块.它自己保存的地址值还是一直在那,所以你free完了后再打印p保存的地址值还跟以前一样.
    

    C++动态内存分配

    C++中多了个class的概念,而类里面有个比较重要的概念是构造函数.而构造函数不能手动去调用,是实例化类时自动调用.如果像C一样用malloc来给某个类动态分配一块内存的话,这个类就不会调用到构造函数了.于是C++里出现了个关键字new,当你使用new动态一块内存时会自动调用构造函数.用完了释放的话就用delete,此时会调用析构函数.

    举个例子吧假如有类Arwen

    Class Arwen
    
    {
    
       public:
    
         Arwen(string str){  name = str;}
    
        string name;
    
       ~Arwen(){ };
    
    }
    
     
    
    Arwen weiwen("csharp");    //这样实例化一个类,是由系统在stack中分配内存并释放内存不用我们管
    
    Arwen*  weiwenhp = new Arwen("cplusplus"); //必须用指针Arwen*,这样才是动态内存分配,由用户自己去申请空间去释放空间.
    
    delete weiwenhp;        //释放内存
    

    内存泄露

    动态分配内存时最容易犯的错,也是最不容易发现的就是内存泄露了啊.

    严格来讲内泄露不是一种错误,它只是没有释放掉申请来的内存,造成了浪费而已.其实很容易用这一点来做一个病毒.你就不停的去申请内存,但都不给释放.到最后内存就会被耗光了.

    研究内存泄露是个比较复杂的话题了,会有很多种情况会导致泄露,也有很多方法去防范.

    举几个简单的例子瞧下

    int * p = new int[88];
    
    delete p;  //这里就内存泄露了,要用delete []p才行.在C中就free(p)就行了
    

    另外在函数中delete还没执行到就退出了也容易内存泄露,比如

    int function(int num)
    
    {
    
             int *p = new[44];
    
            if( num > 111)
    
                return 0;
    
             delete []p;  
    
    return 1;
    
    }
    

    如果num大于100,执行到return 0时就退出了,不会执行到delete.

    当然你也可以自己写一个专属的数组类
    像这样
    头文件

    #include <stdio.h>
    #include <Windows.h>
    
    typedef struct DYNAMICARRAY
    {
        int* pAddr; // 存放数据的地址
        int size; // 当前有多少个元素
        int capacity;  // 容器当前能容纳多少元素
    }Dynamic_Array;
    
    // 动态数组初始化
    Dynamic_Array* Init_Array();
    
    // 插入元素
    void PushBack_Array(Dynamic_Array* arr, int value);
    
    // 根据位置删除元素
    void RemoveByPos_Array(Dynamic_Array* arr, int pos);
    
    // 根据值删除元素
    void RemoveByValue_Array(Dynamic_Array* arr, int value);
    
    // 根据值查找某个元素
    int Find_Array(Dynamic_Array* arr, int value);
    
    // 根据位置返回某个元素
    int At_Array(Dynamic_Array* arr, int pos);
    
    // 打印动态数组
    void Print_Array(Dynamic_Array* arr);
    
    // 释放动态数组的内存
    void FreeMem_Array(Dynamic_Array* arr);
    
    // 清空数组
    void Clear_Array(Dynamic_Array* arr);
    
    // 获得动态数组容量
    int GetCapacity_Array(Dynamic_Array* arr);
    
    // 获得动态数组当前元素的个数
    int GetSize_Array(Dynamic_Array* arr);
    

    cpp文件

    #include "DynamicArray.h"
    
    // 动态数组初始化
    Dynamic_Array* Init_Array()
    {
        Dynamic_Array* arr = (Dynamic_Array*)malloc(sizeof(Dynamic_Array));
        arr->size = 0;
        arr->capacity = 20;
        arr->pAddr = (int*)malloc(sizeof(int) * arr->capacity);
        return arr;
    }
    
    // 插入元素
    void PushBack_Array(Dynamic_Array* arr, int value)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return;
        }
        if (arr->size >= arr->capacity)
        {
            // 1、申请一块更大的内存空间,新空间是旧空间的2倍
            int* pAddr2 = (int*)malloc(sizeof(int) * arr->capacity * 2);
            memset(pAddr2, 0, sizeof(int) * arr->capacity * 2);
            // 2、拷贝数据到新的空间
            memcpy(pAddr2, arr->pAddr, arr->capacity * sizeof(int));
            // 3、释放旧空间的内存
            free(arr->pAddr);
            // 4、更新容量
            arr->capacity = arr->capacity * 2;
            arr->pAddr = pAddr2;
        }
        // 插入新元素
        arr->pAddr[arr->size] = value;
        arr->size++;
    }
    
    // 根据位置删除元素
    void RemoveByPos_Array(Dynamic_Array* arr, int pos)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return;
        }
        if (pos < 0 || pos >= arr->size)
        {
            printf("要删除的元素的位置有误!
    ");
            return;
        }
        for (int i = pos; i < arr->size - 1; i++)
        {
            arr->pAddr[i] = arr->pAddr[i + 1];
        }
        arr->size--;
    }
    
    // 根据值删除元素
    void RemoveByValue_Array(Dynamic_Array* arr, int value)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return;
        }
        int pos = Find_Array(arr, value);
        if (pos != -1 && pos != -2)
        {
            RemoveByPos_Array(arr, pos);
        }
    }
    
    // 根据值查找某个元素
    int Find_Array(Dynamic_Array* arr, int value)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return -1;
        }
        int pos = -1;
        for (int i = 0; i < arr->size; i++)
        {
            if (arr->pAddr[i] == value)
            {
                pos = i;
            }
        }
        return pos;
    }
    
    // 根据位置返回某个元素
    int At_Array(Dynamic_Array* arr, int pos)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return -1;
        }
        return arr->pAddr[pos];
    }
    
    // 打印动态数组
    void Print_Array(Dynamic_Array* arr)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return;
        }
        for (int i = 0; i < arr->size; i++)
        {
            printf("%d ", arr->pAddr[i]);
        }
        printf("
    ");
    }
    
    // 释放动态数组的内存
    void FreeMem_Array(Dynamic_Array* arr)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return;
        }
        if (arr->pAddr != NULL)
        {
            free(arr->pAddr);
        }
        free(arr);
    }
    
    // 清空数组
    void Clear_Array(Dynamic_Array* arr)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return;
        }
        arr->size = 0;
    }
    
    // 获得动态数组容量
    int GetCapacity_Array(Dynamic_Array* arr)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return -1;
        }
        return arr->capacity;
    }
    
    // 获得动态数组当前元素的个数
    int GetSize_Array(Dynamic_Array* arr)
    {
        if (arr == NULL)
        {
            printf("传入的参数有误!
    ");
            return -1;
        }
        return arr->size;
    }
    

    或者

    #include <iostream>
    #include <cstring>
    using namespace std;
    class CArray
    {
        int size;  //数组元素的个数
        int* ptr;  //指向动态分配的数组
    public:
        CArray(int s = 0);  //s代表数组元素的个数
        CArray(CArray & a);
        ~CArray();
        void push_back(int v);  //用于在数组尾部添加一个元素 v
        CArray & operator = (const CArray & a);  //用于数组对象间的赋值
        int length() const { return size; }  //返回数组元素个数
        int & operator[](int i)
        {  //用以支持根据下标访问数组元素,如“a[i]=4;”和“n=a[i];”这样的语句
            return ptr[i];
        };
    };
    CArray::CArray(int s) : size(s)
    {
        if (s == 0)
            ptr = NULL;
        else
            ptr = new int[s];
    }
    CArray::CArray(CArray & a)
    {
        if (!a.ptr) {
            ptr = NULL;
            size = 0;
            return;
        }
        ptr = new int[a.size];
        memcpy(ptr, a.ptr, sizeof(int) * a.size);
        size = a.size;
    }
    CArray::~CArray()
    {
        if (ptr) delete[] ptr;
    }
    CArray & CArray::operator=(const CArray & a)
    {  //赋值号的作用是使 = 左边对象中存放的数组的大小和内容都与右边的对象一样
        if (ptr == a.ptr)  //防止 a=a 这样的赋值导致出错
            return *this;
        if (a.ptr == NULL) {  //如果a里面的数组是空的
            if (ptr)
                delete[] ptr;
            ptr = NULL;
            size = 0;
            return *this;
        }
        if (size < a.size) {  //如果原有空间够大,就不用分配新的空间
            if (ptr)
                delete[] ptr;
            ptr = new int[a.size];
        }
        memcpy(ptr, a.ptr, sizeof(int)*a.size);
        size = a.size;
        return *this;
    }
    void CArray::push_back(int v)
    {  //在数组尾部添加一个元素
        if (ptr) {
            int* tmpPtr = new int[size + 1];  //重新分配空间
            memcpy(tmpPtr, ptr, sizeof(int) * size);  //复制原数组内容
            delete[] ptr;
            ptr = tmpPtr;
        }
        else  //数组本来是空的
            ptr = new int[1];
        ptr[size++] = v;  //加入新的数组元素
    }
    int main()
    {
        CArray a;  //开始的数组是空的
        for (int i = 0; i<5; ++i)
            a.push_back(i);
        CArray a2, a3;
        a2 = a;
        for (int i = 0; i<a.length(); ++i)
            cout << a2[i] << " ";
        a2 = a3;  //a2 是空的
        for (int i = 0; i<a2.length(); ++i)  //a2.length()返回 0
            cout << a2[i] << " ";
        cout << endl;
        a[3] = 100;
        CArray a4(a);
        for (int i = 0; i<a4.length(); ++i)
            cout << a4[i] << " ";
        return 0;
    }
    
  • 相关阅读:
    ES6 新属性 Symbol
    box-shadow 属性详解
    在vue 中 使用 tinymce编辑器
    var let const 结合作用域 的探讨
    防抖和节流在vue中的应用
    分享几个按钮样式
    队列学习
    栈的学习
    Object—常用的遍历
    从零认识Java Package
  • 原文地址:https://www.cnblogs.com/yangj-Blog/p/12945919.html
Copyright © 2011-2022 走看看