目录
1. 概述
数组,就是一个集合,用于存放相同类型的数据元素.
特性: 数组中的每个数据元素的数据类型都是同一个.
数组的内存空间是由所有数据元素的内存空间 连续 组成的.
2. 一维数组
一维数组是指 数组中的每个元素都只有一个下标 的数组
2.1 一维数组的三种定义方式
语法:
数据类型 数组名 [数组长度] ;
给出数组长度,但是暂不赋值 如 int arr[3] ;
此时数组arr中就包含3个元素,分别是 arr[0] , arr[1] 和 arr[2]. [0] / [1] / [2]就被称为它们的下标
注意下标是从0开始计数的,这意味着 数组长度为n的一维数组的 最大下标为[n-1]
但是我们并没有给这些元素赋值,这些值默认都为0.(但如果没有之前没有清空内存空间,也可能是其他值.)
数据类型 数组名 [ ] = {值1,值2,值3,……};
给出具体的值,但是数组长度由程序自己计数得出
数据类型 数组名 [数组长度] = {值1,值2,值3,……};
同时明确数组长度和赋值
这时,若明确赋值的元素数量小于标明的数组长度,则仍以数组长度为准分配内存,未赋值的元素将赋值为0 (bool 类型数组也是赋值为0,字符型和字符串型则为ASCII码0对应的字符)
总结起来就是,定义一维数组的时候,一定要明确数组的长度,要不就直接给出数组长度,要不就列出数组中包含的全部元素.这是因为程序在创建数组变量时需要分配好确定大小的内存空间
也就是说,创建数组变量的时候,这个数组未来可能会用到的所有内存空间就已经被占用了.
2.2 一维数组的 数组名 的作用
2.21.可以统计整个数组在内存中占用的空间大小
利用sizeof关键字可以得出整个数组占用的内存大小.
int arr[10] ;
cout << sizeof(arr) << endl;
输出结果为40,也就是10个整型变量所占的内存空间大小(每个4字节).
拓展:
int arr[10] ;
cout << sizeof(arr[5]) << endl;
在数组名后面加一个合法的下标,再sizeof,可以得到单个元素所占的内存空间大小.
此处显然为4个字节.
显然,它们之间满足这样的关系:
整个数组所占内存大小 / 单个元素所占内存大小 = 数组长度 = 元素个数
int arr[] = {1,2,5,3,4,7,9,8,6};
cout << sizeof(arr)/sizeof(arr[0]) << endl;
当你懒得数数组有多长的时候就可以这样得到数组长度.
2.2.2.可以获取数组在内存中的首地址
直接 cout << 数组名 << endl;
或者 cout << &数组名 << endl; (&为取址符,也就是说这里取址符可以省略.)
就可以得到十六进制的数组首地址. 如果需要十进制的地址,可在数组名前加(int)
特别地,数组中第一个元素的地址就是整个数组的首地址.
可以通过 cout << &数组名[0] << endl; 来验证.
注意,此处的取址符就不可以省略了.
#include <iostream>
using namespace std;
int main()
{
int arr[9] = { 1,2,5,3,4,7,9,8,6 };
//整个数组占用的内存大小
cout << sizeof(arr) << endl;
//单个元素占用的内存大小
cout << sizeof(arr[0]) << endl;
//数组长度=总内存大小/单个元素内存大小
cout << sizeof(arr) / sizeof(arr[0]) << endl;
//获取数组首地址
cout << arr << endl;
//获取数组首地址(十进制)
cout << (int)arr << endl;
//数组中第一个元素的地址
cout << &arr[0] << endl;
cout << (int)&arr[0] << endl;
return 0;
}
2.2.3 其他
注意数组名被视为一个常量,强行对数组名赋值会报错.如
int arr[5];
arr = 100; //错误,数组名为常量,不能赋值
2.3 一维数组案例
2.3.1 找最大值
给定一个数组,求其中最大的元素的值,并求出该元素是数组中的第几个元素.
#include <iostream>
using namespace std;
int main()
{ //求数组中的最大值
//创建一个数组
int arr[10] = { 2,4,5,3,7,0,9,6,1,8 };
//求数组长(直接用10也行,毕竟这里知道大小)
int length = sizeof(arr) / sizeof(arr[0]);
//定义最大值变量
int max = 0;
//找最大值是数组中的第几个元素:
int ordinal = 0;
//通过下标自增的方式,循环遍历数组中的每个元素,从下标0开始,到(长度-1)结束
for (int num = 0; num < length; num++)
{
//当某个数组中的元素比当前最大值还要大时,用元素值覆盖最大值
if( arr[num] > max )
{
max = arr[num];
ordinal = num+1; //第n个元素的下标为n-1,所以下标需要+1才能得到序数
}
}
cout << "最大值为:" << max << endl;//输出最大值
cout << "最大值位于数组中的第 " << ordinal << " 个元素" << endl;
return 0 ;
}
当然, 也可以用三目运算符稍微简化一下.
如果你把这段代码直接衔接在上面那段代码中,记得要把max和ordinal 重新初始化为0.
//也可以用三目运算符进行简化
for (int num1 = 0; num1 < length; num1++)
{
//如果最大值比数组元素大,就返回最大值,否则返回数组元素
max = (max > arr[num1] ? max : arr[num1]);
//第n个元素的下标为n-1,所以下标需要+1才能得到序数
ordinal = (max > arr[num1] ? ordinal : num1+1);
}
cout << "最大值为:" << max << endl;//输出最大值
cout << "最大值位于数组中的第 " << ordinal << " 个元素" << endl;
2.3.2 元素逆置
如果想要把整个数组序列倒过来输出要怎么写呢?
//把整个数组序列倒过来再输出
//定义一个暂存空间方便交换数字
int temp = 0;
//定义未交换的数组部分的结尾,因为已经首位交换过的数字不用再参与交换了,数值为(数组长度-1)
int end = length-1;
//
int arr1[10] = { 9,4,5,3,7,2,0,6,1,8 };
//先遍历输出一次原序列
for (int num = 0; num < length; num++)
{
cout << arr1[num] << "\t";
}
cout << endl;
//当首下标小于尾下标时执行,若首尾下标相等说明是同一个元素,若大于则说明首下标已越过尾下标,不用再交换
//循环结束之前首下标要自增,尾下标要自减,有两步操作,所以不能写末尾循环体了,要放到循环语句里面
for (int start = 0; start < end; )
{
//暂存首下标元素
temp = arr1[start];
//尾下标覆盖首下标
arr1[start] = arr1[end];
//暂存的首下标覆盖尾下标
arr1[end] = temp;
start++;//移动始位
end--;//移动末位
}
//检查交换结果
for (int num = 0; num < length; num++)
{
cout << arr1[num] << "\t";
}
cout << endl;
2.4 冒泡排序
冒泡排序是最常用的排序算法,用于对数组内的元素按数值大小进行升序排序(逐渐增大)
规则: 从第一个元素(下标为0)开始,和下一个元素进行比较,若某个元素比下一个元素大,则互换它们的位置.互换完成后,从第二个元素继续开始比较.
直至最后一个元素的比较完成,此时最后一个元素一定是数组中的最大值.
然后再开始新一轮的排序,但是上一次已经确定了一个最大值,因此这一轮比较次数-1.
这样数组中的倒数第二个元素的值一定是数组中第二大的.
如此反复直到数组中的所有数字从左到右依次增大,完成排序.
#include <iostream>
using namespace std;
int main()
{
//冒泡排序
//从第一个元素开始,和下一个元素进行比较,若下一个元素较小,则将这两个元素位置互换
//创建数组
int arr[10] = { 8,5,7,1,4,2,3,0,9,6 };
//求数组长度
int n = sizeof(arr) / sizeof(arr[0]);
//先遍历一遍数组把完整序列输出一下
for (int i = 0; i < 10; i++)
{
cout << arr[i] << " ";
}
cout << endl;
//两层循环结构
//大循环,每次都要完成一整轮排序,最右边浮出一个最大值(上一个最大值视为移出)
//要对n个数全部排序,则至多需要浮出(n-1)次最大值,即进行(n-1)轮排序,有10个数,则0~8共9轮
for (int i = 0; i < n-1 ; i++)
{
//小循环,每次比较两个数,则第i轮共有(n-i)个数需要排序,因为第一轮i=0
//两个数才能比一次,所以需要比(n-i-1)次
//第一次又是0,共n个数,第i轮需要排(n-i)个数,需要比(n-i-1)次,所以0~(n-i-2) 共(n-i-1)次
for (int j = 0; j < n-i-1; j++)
{
//比较和下一个数的大小,若下一个数更小,则位置互换
if(arr[j]>arr[j+1])
{
int temp = arr[j+1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
//再输出一次看看排序结果
for (int i = 0; i < 10; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
3. 二维数组
3.1 二维数组的四种定义方式
语法:
数据类型 数组名 [行数][列数] ;
数据类型 数组名 [行数][列数] = {{数据1,数据2},{数据3,数据4}}; (最为直观,建议使用)
数据类型 数组名 [行数][列数] = {数据1,数据2,数据3,数据4};
数据类型 数组名 [ ][列数] = {数据1,数据2,数据3,数据4};
3.2 二维数组的遍历顺序
0列 | 1列 | 2列 | |
0行 | arr[0][0] 1 | arr[0][1] 2 | arr[0][2] 3 |
1行 | arr[1][0] 4 | arr[1][1] 5 | arr[1][2] 6 |
当使用第一种方法定义二维数组时,直接输出这些元素的值,发现并不为0.
这说明程序只给这些元素分配了内存空间,但是并没有重置其中的数据.
当使用第二种方法定义二维数组时,若列子集中的元素个数小于列数,则默认用0填充剩余的空位.
若列子集的个数小于行数,则默认用0填充剩余的列子集中的所有空位.
#include <iostream>
using namespace std;
int main()
{
int arr[3][6] =
{
{1,2},
{3,4,5}
};
for (int a = 0; a < 3; a++)
{
for (int b = 0; b < 6; b++)
{
cout << arr[a][b] << " " ;
}
cout << endl;
}
return 0;
}
当你使用第三种方法定义二维数组时,若给出值的元素个数小于行数*列数,则优先用给出的值,从第0行第0列开始赋值,填满一行后再进行下一行,剩余的空位用0填充.
#include <iostream>
using namespace std;
int main()
{
int arr[3][6] = { 1,2,3,4,5,6,7,8,9 };
for (int a = 0; a < 3; a++)
{
for (int b = 0; b < 6; b++)
{
cout << arr[a][b] << " " ;
}
cout << endl;
}
return 0;
}
当你使用第四种方法定义二维数组时,与第三种类似,若给出值的元素个数小于行数*列数,则优先用给出的值,从第0行第0列开始赋值,填满一行后再进行下一行,剩余的空位用0填充.
但与第三种方法不同的是,若第三种方法中存在全空的行,程序仍然会为这些行分配内存并赋值0.
但第四种方法的行数是由程序计算得出的,当程序将提供的最后一个数值赋值给对应元素之后,就只会填满当前行中剩下的列,不会新起一行(即不会有全空的行).
需要注意的是, 用这种方式定义的数组在访问之前需要先求出行数!
否则,一旦循环条件/循环次数 与行数不匹配,就会访问到不属于该数组的内存空间.
#include <iostream>
using namespace std;
int main()
{
int arr[][6] = { 1,2,3,4,5,6,7,8,9 };
int row = sizeof(arr3)/sizeof(arr3[0]); //总内存大小除以第一行内存大小,得行数
cout << row << endl;
for (int e = 0; e < row; e++)
{
for (int f = 0; f < 6; f++)
{
cout << arr3[e][f] << " ";
}
cout << endl;
}
return 0;
}
3.3 二维数组的数组名
3.3.1 查看二维数组所占内存大小
int arr[2][3] =
{
{1,2,3},
{4,5,6}
};
cout << sizeof(arr) << endl; //整个二维数组占用的内存
cout << sizeof(arr[0]) << endl; //二维数组第一行占用的内存
cout << sizeof(arr[1][2]) << endl; //二维数组第二行第三个元素占用的内存
int row = sizeof(arr) / sizeof(arr[0]) ; //总大小除以单行大小, 得到行数
int column = sizeof(arr[0]) / sizeof(arr[0][0]) ; //单行大小除以单个元素大小, 得到列数
3.3.2 获取二维数组的首地址
同理可以取得整个二维数组的首地址, 每行的首地址, 每个元素的首地址.
当取址对象为二维数组或者二维数组的行时,取址符可以省略.
但当取址对象为具体的某个元素时,取址符不可以省略,否则会输出的不是元素的地址,而是元素的值.
这一点与一维数组是相同的.
int arr[2][3] =
{
{1,2,3},
{4,5,6}
};
cout << arr0 << endl; //整个二维数组的首地址
cout << &arr0 << endl; //同上一行
cout << arr0[0] << endl; //二维数组第一行的首地址
cout << &arr0[0] << endl; //同上一行
cout << &arr0[0][0] << endl; //二维数组第一个元素的首地址
3.4 二维数组案例
有三名同学, 每人都参加了语文,数学,英语 三门考试, 请用二维数组打印他们的成绩,并打印出总分.
#include <iostream>
using namespace std;
int main()
{
//三人考试成绩,用二维数组存放,行为某人的三科成绩,列为某科的三人成绩
int score[3][3] =
{
{100,99,98},
{90,80,70},
{60,40,20}
};
//三人姓名,用一维数组存放
string name[3] = { "张三","李四","王五" };
//三个科目的名称,用一维数组存放
string course[3] = { "语文","数学","英语" };
//总分,用一维数组存放
int sum[3] = { 0,0,0 };
//大循环,每个循环输出一个人的全部成绩
for (int a = 0; a < 3; a++)
{
cout << name[a] << "的考试成绩为:\t" ;
//小循环,每个循环输出某个人的一科成绩
for (int b = 0; b < 3; b++)
{
cout << course[b] << score[a][b] << "分\t"; //输出各科成绩
sum[a] += score[a][b];
}
cout << "总分:" << sum[a] << endl; //写完一个人的才换行
}
return 0;
}