在复习到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;
}