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;
}
原文地址:https://www.cnblogs.com/yangj-Blog/p/12945919.html