一.数组
1.数组的概念:
是由一批相同类型的元素的集合所组成的数据结构
2.数组的定义语法:
数据类型 数组名[数组长度];
其中数据类型表示数组这个容器内要放什么样的数据,数组名就是容器的名字,数组长度就是代表容器的大小
3.下标索引的概念:
数组内的每个“格子”都有自己的编号,也就是下标索引(注意下标索引是从0开始的,也就是说第一个数据的索引是0)
所以我们可以通过数组名[下标索引]的方式来访问我们想要的位置上的数据
二.数组的特点
1.任意类型都可以构建数组,包括下方的各种数据类型
①:基本数据类型如:int, float, double, char, string, bool
②:复合数据类型如:结构体,联合体
③:指针类型如:int*, char*, float*
④:枚举类型如:enum
2.固定大小
C++中的数组一旦完成了定义,那么它的大小就已经固定了,无法进行修改
3.内存连续并且有序
数组内存的空间是连续分配的,并且每个元素分配的大小取决于存放类型
因此我们可以通过sizeof(数组) / sizeof(数组中的某个元素)获得数组的元素个数
4.元素值可修改
我们可以通过数组名[下标索引]对锁定的元素进行赋值,对该处位置的存储值进行修改
5.数组变量不记录数据
数组变量本身并不记录元素,而是记录从第一个元素开始的内存地址,然后通过记录的内存地址依次获取之后的元素(因为数组的内存是连续并且有序的)
三.数组的遍历:
对数组进行遍历是以后我们经常会用到的功能,不管使用for循环还是使用while循环都能实现我们想要的效果,但是具体到时候使用什么循环还是要具体的情况,我在这里仅仅只是简单举例
1.for循环遍历
#include"iostream"
using namespace std;int main() {int ans[] = {1, 2, 3, 4, 5};for (int i = 0; i < sizeof(ans) / sizeof(ans[0]); i++) {cout << ans[i] << endl;}
}
2.while循环遍历:
#include"iostream"
using namespace std;int main() {int ans[] = {1, 2, 3, 4, 5};int len = sizeof(ans) / sizeof(ans[0]);int i = 0;while (i < len) {cout << ans[i] << endl;i++;}
}
3.for循环的高级写法
原理:
通过临时变量在每一次循环体中记录数组的每一个元素,数组有几个元素循环体就执行几次
程序中的演示:
#include"iostream"
using namespace std;int main() {for (元素类型 临时变量名 : 数组变量) {代码; }
}
举例:
#include"iostream"
using namespace std;int main() {int ans[] = {1, 2, 3, 4, 5};for (int i : ans) {cout << i << endl;}
}
注意:这种方式在版本较低的情况下用不了
四.字符数组
C++中定义字符串可以有以下三种方式:
"String";
char str[] = "String";
char * str = "String";
其中第一种方式和第二种方式定义的字符串的本质都是字符数组,它们的存储机制为:将每一个字符都作为一个元素存入字符数组中,然后在字符数组中的最后一个元素之后添加一个"\0"(空字符)作为结束标记
五.多维数组
1.概念:
对数组进行嵌套,也就是说数组内的每个元素都是数组,根据嵌套的层次可以有二维,三维,四维,乃至更多维度的数组
2.定义语法:
数据类型 数组名[一维][二维]...[n维];
// 如我们定义一个二维int型数组:
int sum[5][5];
3.多维数组的遍历:
我们可以通过for和while循环对多维数组进行遍历,其中我们需要注意的基本原则是:
①:有着几维我们就至少写上多少层嵌套循环
②:每一层循环都对应一个维度
③:一般使用for循环更加方便
④:遍历的举例:
#include"iostream"
using namespace std;int main() {int nums[3][3];int num = 1;for (int i = 0; i < sizeof(nums) / sizeof(nums[0]); i++) {for (int j = 0; j < sizeof(nums[0]) / sizeof(nums[0][0]); j++) {nums[i][j] = num;num++;cout << nums[i][j];}cout << endl;}
}
六.指针
1.指针在C++中的重要作用:
指针是C++中至关重要的组成部分,它具有强大的功能,本质是协助程序员完成内存的直接操纵
2.指针的概念:
特定数据类型在内存中的存储地址,也就是内存地址(注意指针只是一个逻辑概念,实际的应用其实应该是指针变量)
3.指针变量的定义语法:
①:
数据类型* 指针名; // 声明
指针名 = &指向的地址; // 赋值
②:
变量类型* 指针名 = 指向的地址; // 定义与赋值一同进行
注意:
①:*的含义:
在声明时:*指针名表示的是变量,这是一个指针变量,其中存的是内存地址
int* p; // 声明了一个指针变量,其变量名为p
在使用时:*指针名表示取出指针所指向的内存区域的数据
#include"iostream"
using namespace std;int main() {int num = 10;int* p = #int ans = *p; // 取出了p指针变量所存储的地址中的值 cout << ans << endl;
}
②:&符号表示取变量的内存地址,是一个取内存地址的单目操作符
4.野指针和空指针
①:野指针:
被声明但是没有进行初始化(赋值)的指针,这个指针会指向随机的内存空间,可能会导致未知的问题
为了避免野指针,我们应该养成及时初始化的习惯,或者将指针置为空指针
// 置为空指针的方法:
int* p = NULL; // NULL是C++中内置的宏,表示空,本质是0
int* p = nullptr; // nullptr是C++11标准引入的关键字,表示指针为空
②:空指针表示不指向任何地方,那么也就不会再出现安全问题
空指针只在需要指针,但是需要延迟赋值的场景下作为过渡进行使用
我们在使用指针时一定要注意进行初始化,养成好习惯!
5.指针运算:
尽管指针变量内存储的是内存地址,但是仍然可以进行基础的数学计算,指针运算是对指针的基础型操作,非常适合操纵数组并配合做动态内存分配
①.指针加减运算:
指针进行加减运算的结果和指针指向内存区域的数据类型相关,以加法为例:
char类型的指针+1表示地址+1(单位为字节)
int类型的指针+1表示地址+4(单位仍为字节)
double类型的指针+1表示地址+8
...
就从上方的几个例子就可以看出,指针的加减法(加减n)就是内存地址加减n * 该类型所占的字节量
②.使用指针和循环对数组进行遍历:
#include"iostream"
using namespace std;int main() {int nums[5] = {1, 2, 3, 4, 5};int* p = nums;for (int i = 0; i < sizeof(nums) / sizeof(nums[0]); i++) {cout << *(p + i) << endl;}
}
七.动态内存分配
1.概念:
由程序员手动进行的内存空间的分配,内存空间的释放等内存管理操作
2.静态内存分配:
在C++代码中,变量、数组等对象的创建是由C++自动分配内存的,称之为静态内存分配,而静态内存管理是不会进行内存空间的自动清理的(不会进行垃圾回收),因此我们需要手动地进行内存管理(手动分配,用完清理)
3.动态内存分配的方法:
①:new:
new运算符用于申请并且分配内存空间,并且提供指向该空间的指针
基本语法:
new type; // 申请普通变量空间
new type[n]; // 申请数组空间
②:delete:
delete运算符用于释放内存(仅可用于new运算符申请的内存区域)
基本语法:
delete 指针名; // 删除普通变量空间
delete[] 指针名; // 删除数组空间
③:一次连贯的内存分配:
八.数组元素的移除
在C++中的内置中并未提供对数组元素进行增加(插入)、移除的功能,这些功能需要手动进行实现(vector容器提供)
1.移除元素的思路:
①:通过new操作符申请新数组的内存空间,并且复制数据到新数组
②:通过delete删除旧数组的空间占用
③:将旧数组的指针指向新数组的地址
2.移除元素实例:
#include"iostream"
using namespace std;int main() {int *pOld = new int[5] {1, 2, 3, 4, 5};int *pNew = new int[4];for (int i = 0; i < 5; i++) {if (i == 3)continue;if (i > 3)pNew[i - 1] = pOld[i];else pNew[i] = pOld[i];}for (int i = 0; i < 4; i++) {cout << *(pNew + i) << endl;}
}
九.数组元素的插入
1.插入数组元素的思路:
①:创建新数组,将老数组元素和新插入元素一起复制到新数组当中
②:新元素在指定位置进行插入(意味着老数组元素要配合做下标增加)
③:插入数组元素案例:
#include"iostream"
using namespace std;int main() {int *pOld = new int[5] {1, 2, 3, 4, 5};int *pNew = new int[7];int reset = 0;for (int i = 0; i < 7; i++) {if (i == 1) {pNew[i] = 1;reset++;continue;} else if (i == 3) {pNew[i] = 3;reset++;continue;}pNew[i] = pOld[i - reset];}for (int i = 0; i < 7; i++) {cout << *(pNew + i) << endl;}
}
十.指针悬挂
1.概念:
指针指向的区域已经被回收,这种问题被称为指针悬挂
2.注意:
①:不要轻易地进行指针之间的相互赋值
②:delete回收空间之前,要确保此空间一定不会再被使用
十一.const指针
const是C++的关键字,被译为常量,const指针表示常量指针
1.指向const的指针:
表示指向区域的数据是不变的,但可以更换指向
语法:
const 数据类型 *指针名;
数据类型 const *指针名;
也就是说,可以改变当前指针所指向的地址,但是不能更改当前指针指向地址中的数据
2.const指针:
表示指针本身不可更改,但指向的数据可以更改
语法:
数据类型 * const 指针名 = 地址; // 这里必须进行初始化,因为之后指针的地址不可修改
也就是说,const指针当前所指向的地址是不可改变的,但是其中的数据可以进行更改