数组
1.概念
数组是在内存中的一片连续的存储区域,用来存储若干个相同类型
数组可以用来存储任意类型的数据,但是每个数组中的数据类型必须相同
数组是一个定长的容器,一旦初始化完成,长度不能改变
数组中每个值称为元素
每个元素在数组的位置成为下标,下标从
0开始递增
定义数组
修饰符 数组名[数组长度];
修饰符 数组名[数组长度] = {值1, 值2, 值3,....}2.基本使用
2.1 定义数组
定义指定长度的数组,因为没有初始值,所以此时数组中的元素是不安全的
int arr[10];
cout << arr[0] << endl; // 可能会打印地址中残留的数据定义数据的同时,给出初始值
int arr[5] = {1, 2, 3, 4, 5};若数组中元素的数量小于数组的长度,后面的值会自动填充为 0
int arr[5] = {1, 2, 3}; // {1,2,3,0,0}若定义数组不指定长度,此时数组的大小则为初始化时元素的个数
int arr[]={1,2,3,4,5};2.2 下标
2.2.1 下标的概念
每个元素在数组的位置成为下标,也可成为索引,下标从0开始递增,通常用index来表示
通过下标,我们可以访问和修改数组中的值
int arr[5]={1,2,3,4,5};
2.2.2 访问元素
通过数组名[下标]的语法访问数组中特定下标的值,数组访问不可越界
#include<iostream>
using namespace std;
int main() {
int arr[10] = { 1,2,3,4,5 };
cout << arr[0] << endl; // 1
cout << arr[1] << endl; // 2
cout << arr[2] << endl; // 3
cout << arr[3] << endl; // 4
cout << arr[4] << endl; // 5
cout << arr[5] << endl; // 0
cout << arr[6] << endl; // 0
cout << arr[7] << endl; // 0
cout << arr[8] << endl; // 0
cout << arr[9] << endl; // 0
// cout << arr[10] << endl; // 越界访问
return 0;
}3.数组的长度
在c++中,数组没有一个现成的长度属性供我们使用,但可以通过计算获取数组的长度
sizeof()方法可以获取一个数据类型或者一个变量的空间占用大小
那么一个数组的空间大小除以数组中变量类型的空间大小,就是数组的长度
int len = sizeof(数组名)/sizeof(数组类型)例如
#include<iostream>
using namespace std;
int main() {
int arr[5] = { 1,2,3,4,5 };
int len = sizeof(arr) / sizeof(int);
cout << len << endl; // 5
double arr1[10] = { 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0 };
cout << sizeof(arr1) / sizeof(double) << endl; // 10
return 0;
}4.数组的内存分析
4.1 元素的地址规律
我们定义一个数组,并打印每个值的地址
#include<iostream>
using namespace std;
int main() {
int arr[5] = { 1,2,3,4,5 };
cout << &arr[0] << endl;
cout << &arr[1] << endl;
cout << &arr[2] << endl;
cout << &arr[3] << endl;
cout << &arr[4] << endl;
return 0;
}出来的是一串16进制的地址,看不出什么名堂

把地址转化为int10进制
#include<iostream>
using namespace std;
int main() {
int arr[5] = { 1,2,3,4,5 };
cout << (int)&arr[0] << endl;
cout << (int)&arr[1] << endl;
cout << (int)&arr[2] << endl;
cout << (int)&arr[3] << endl;
cout << (int)&arr[4] << endl;
return 0;
}每次打印的值肯定不同,但是可以发现,每个值相差4,也就是一个int所占的空间

所以可以得出:数组是在内存中的一片连续的存储空间
4.2 数组的地址
打印下数组的地址
#include<iostream>
using namespace std;
int main() {
int arr[5] = { 1,2,3,4,5 };
cout << (int)&arr[0] << endl;
cout << (int)&arr[1] << endl;
cout << (int)&arr[2] << endl;
cout << (int)&arr[3] << endl;
cout << (int)&arr[4] << endl;
cout << (int)arr << endl;
return 0;
}
可以发现,数组中首元素的地址就是数组本身的地址
TIP
通过指针访问数组
因为数组中首元素的地址就是数组本身的地址,所以将指针指向数组不需要加&
int main() {
int arr[5] = { 1,2,3,4,5 };
int* p = arr; // 相当于 int* p = &arr[0];
cout << *p << endl; // 1
cout << ++*p << endl; // 2
return 0;
}4.3 数组作为函数参数
将数组作为参数,并在函数中打印此数组
#include<iostream>
using namespace std;
void printArray(int arr[]) {
cout << arr << endl;
}
int main() {
int arr[5] = { 1,2,3,4,5 };
printArray(arr);
return 0;
}发现打印的是一个地址

既然是地址,就可以使用指针指向这个地址,并可访问其中的值。我们使用指针来打印这个地址中的值
#include<iostream>
using namespace std;
void printArray(int* arr) {
cout << *arr << endl;
}
int main() {
int arr[5] = { 1,2,3,4,5 };
printArray(arr);
return 0;
}发现打印的是数组中第一个元素的值

所以可以得出:数组在函数中作为参数传递时,传递的是数组第一个元素的地址
因此,如果需要在函数中遍历数组,需要再传递一个数组的长度
#include<iostream>
using namespace std;
void printArray(int* arr,int len) {
for (int i = 0;i < len;i++) {
cout << arr[i] << endl;
}
}
int main() {
int arr[5] = { 1,2,3,4,5 };
printArray(arr,sizeof(arr)/sizeof(int));
return 0;
}
4.4 总结
- 数组是在内存中的一片连续的存储空间
- 数组中首元素的地址就是数组本身的地址
- 数组在函数中作为参数传递时,传递的是数组第一个元素的地址
5.数组的遍历
5.1 下标遍历
#include<iostream>
using namespace std;
int main() {
int arr[5] = { 1,2,3,4,5 };
int length = sizeof(arr) / sizeof(int);
for (int i = 0;i < length;i++) {
cout << arr[i] << endl;
}
return 0;
}5.2 元素迭代
#include<iostream>
using namespace std;
int main() {
int arr[5] = { 1,2,3,4,5 };
for (int ele : arr) {
cout << ele << endl;
}
return 0;
}ele:迭代元素,也就是数组每一项的值,ele名是自己定义的
TIP
- 只有在定义数组的部分才能使用此方法遍历数组,若通过函数传参,那么函数的形参只是一个值,不可遍历
- 在迭代过程中不可通过迭代元素修改数组中元素的值,因为迭代元素和数组元素地址不同
int main() {
int arr[5] = { 1,2,3,4,5 };
for (int ele : arr) {
ele++;
cout << ele << endl; // 2,3,4,5,6
}
for (int ele : arr) {
cout << ele << endl; // 1,2,3,4,5
}
return 0;
}上面代码,看似修改了元素的值,但修改的只是当前循环中迭代元素的值,元素本身内部的值没有改变。
通过引用可以修改数组本身元素的值
int main() {
int arr[5] = { 1,2,3,4,5 };
for (int &ele : arr) {
ele++;
}
for (int ele : arr) {
cout << ele << endl; // 2,3,4,5,6
}
return 0;
}6.数组中的算法
6.1 排序
6.1.1 选择排序
选择排序的思想是先从第一位往后找,找出数组中的最小值,把他和第一位交换位置,再从第二位找出数组中的最小值,把他和第二位交换,以此类推

#include<iostream>
using namespace std;
void swap(int& num1, int& num2) {
int temp = num1;
num1 = num2;
num2 = temp;
}
void choose_sort(int* arr, int len) {
for (int i = 0;i < len;i++) {
int minIndex = i;
for (int j = i + 1;j < len;j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
swap(arr[minIndex], arr[i]);
}
}
int main() {
int arr[10] = { 5,2,9,4,1,3,5,10,6,9 };
int len = sizeof(arr) / sizeof(int);
choose_sort(arr, len);
for (int val : arr) {
cout << val << ","; // 1,2,3,4,5,5,6,9,9,10,
}
return 0;
}6.1.2 冒泡排序
冒泡排序的思想是两两比较,若前面的值大于后面的值,则交换位置,这样每一轮交换之后,最大的值会依次往数组的最后面排

#include<iostream>
using namespace std;
void swap(int& num1, int& num2) {
int temp = num1;
num1 = num2;
num2 = temp;
}
void bubble_sort(int* arr, int len) {
for (int i = 0;i < len-1;i++) {
for (int j = 0;j < len-1-i;j++) {
if (arr[j] > arr[j+1]) {
swap(arr[j], arr[j+1]);
}
}
}
}
int main() {
int arr[10] = { 5,2,9,4,1,3,5,10,6,9 };
int len = sizeof(arr) / sizeof(int);
bubble_sort(arr, len);
for (int val : arr) {
cout << val << ","; // 1,2,3,4,5,5,6,9,9,10,
}
return 0;
}TIP
j < len-1-i的意义:
每一轮外层循环时,都会拿数组中相邻的两个值相互比较,但是当倒数第二和和倒数第一个值比较完之后,我们不可能再拿倒数第一个值和后面的值比较了,因为后面没值了,所以要-1
每一轮外层循环完成之后,最大的值已经排在了最后面,此时没必要再拿当前循环总最大值和后面的比较,肯定没后面的值大,所以每一轮外层循环之后,就已经找出了i个最大值,就需要-i
6.2 数组元素查找
元素查找就是给定一个值,让我们在一个数组中查找与该值相同的元素,找到就返回该元素的下标,否则返回-1
6.2.1 顺序查询
顺序查询就是从数组的开头往后一个一个找
#include<iostream>
using namespace std;
int indexOf(int* arr, int len,int val) {
for (int i = 0;i < len;i++) {
if (val == arr[i]) return i;
}
return -1;
}
int main() {
int arr[10] = { 5,2,9,4,1,3,5,10,6,9 };
int len = sizeof(arr) / sizeof(int);
cout << indexOf(arr, len, 6) << endl; // 8
cout << indexOf(arr, len, 9) << endl; // 2
cout << indexOf(arr, len, 3) << endl; // 5
cout << indexOf(arr, len, 22) << endl; // -1
cout << indexOf(arr, len, 33) << endl; // -1
return 0;
}6.2.2 二分查找
二分查找仅适用于已经排序好的数组,其思想是拿需要查找的值和数组中间的那个值比较,再根据比较的情况,把比较的范围缩小到数组的左半边或者右半边,以下是二分查找和顺序查找的比较图

#include<iostream>
using namespace std;
int binary_search(int* arr,int len,int val) {
int min = 0, max = len - 1;
while (min <= max) {
int mid = (min + max) / 2;
if (arr[mid]>val) {
max = mid - 1;
}
else if(arr[mid] < val) {
min = mid+1;
}
else {
return mid;
}
}
return -1;
}
int main() {
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int len = sizeof(arr) / sizeof(int);
cout << binary_search(arr,len,2) << endl; // 2
cout << binary_search(arr,len,5) << endl; // 5
cout << binary_search(arr,len,6) << endl; // 6
cout << binary_search(arr,len,7) << endl; // 7
cout << binary_search(arr,len,10) << endl; // -1
cout << binary_search(arr,len,15) << endl; // -1
return 0;
}7.浅拷贝和深拷贝
7.1 浅拷贝
浅拷贝是地址拷贝,拷贝到的是数组的首元素地址。这样修改一个数组中每个元素的值,另一个数组也会被更改
TIP
如下可以看到,把一个数组直接赋值给另一个数组,当我们修改其中一个数组中的值,另一个数组的值也被修改了
#include<iostream>
using namespace std;
void print_array(int *arr,int len) {
for (int i = 0;i < len;i++) {
cout << arr[i];
if (i < len-1) {
cout << ',';
}
else {
cout << '\n';
}
}
}
int main() {
int arr[] = { 1,2,3,4,5 };
int* arr1 = arr; // 接着赋值地址给arr1
print_array(arr, sizeof(arr) / sizeof(int));
print_array(arr1, sizeof(arr) / sizeof(int));
arr[2] = 666;
print_array(arr, sizeof(arr) / sizeof(int));
print_array(arr1, sizeof(arr) / sizeof(int));
return 0;
}
7.2 深拷贝
深拷贝:定义一个新的数组,长度与原来的数组相同,将原来数组中的每一个元素依次拷贝到新的数组中。
TIP
而一步一步把一个数组里面的值,一个一个的赋值到另一个数组,这样修改任意一个数组,对另一个数组都不会有影响
#include<iostream>
using namespace std;
void copy(int* arr, int len,int* arr2,int len2) {
if (len2 <= len) {
for (int i = 0;i < len2;i++) {
arr2[i] = arr[i];
}
}
}
void print_array(int *arr,int len) {
for (int i = 0;i < len;i++) {
cout << arr[i];
if (i < len-1) {
cout << ',';
}
else {
cout << '\n';
}
}
}
int main() {
int arr[] = { 1,2,3,4,5 };
int arr1[5];
copy(arr, sizeof(arr) / sizeof(int), arr1, sizeof(arr1) / sizeof(int));
print_array(arr, sizeof(arr) / sizeof(int));
print_array(arr1, sizeof(arr) / sizeof(int));
arr[2] = 666;
arr1[3] = 777;
print_array(arr, sizeof(arr) / sizeof(int));
print_array(arr1, sizeof(arr) / sizeof(int));
return 0;
}
8.二维数组
二维数组就是一个数组里面嵌套了若干个小数组
修饰符 数组名[行数][列数] = {
{值1,值2,...},
{值1,值2,...},
{值1,值2,...},
// ...
};8.1 定义二位数组
1.声明不赋值
int arr[3][5];2.声明并赋值
int arr[3][5]={
{1,2,3,4,5},
{1,2,3},
{1,2,3,4,5}
};TIP
若二维数组中的小数组元素不足时,会自动补0,多了则会报错
3.类似一维数组写法
int arr[3][5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};TIP
这种写法会被自动转化为如下写法
int arr[3][5]={
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15}
};也就是说会自动根据行数和列数,转化为一个正常的二维数组,当最后一个小数组元素不足时,会自动补0,多了则会报错
4.只声明列数,不声明行数
int arr[][5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17};TIP
这种写法会根据每一列元素的数量,自动转化为相应数量的行数
int arr[][5]={
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17}
};8.2 二维数组的访问
通过指定的行数和列数便可访问二维数组中的指定元素
数组名[行数-1][列数-];例如
int main() {
int arr[3][5] = {
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15}
};
cout<<arr[0][2]<<endl; // 第一行第三列:3
cout<<arr[1][3]<<endl; // 第二行第四列:9
cout<<arr[2][4]<<endl; // 第三行第五列:15
return 0;
}