数组
如果有100个互不相关联的数据,可以分别把它们存放到100个变量中,但是如果这些数据是有内在联系的,是具有相同属性的(如:100个学生的成绩),那么就可以把这些数据看作一个整体,也就是数组了。
所谓数组,就是用一个统一的名字代表这批数据,而用序号或下标来区分各个数据。例如:s代表学生成绩这组数据,s就是数组名,用s1,s2,s3分别代表学生1,学生2,学生3的成绩,s右下角的数组1,2,3用来表示该数据在数组中的序号,也称为下标,数组中的数据成为数据元素。
简单来说:数组是有序数据的集合。要寻找一个数组中的某个元素需要数组名和下标两个元素。数组也是有类型的,同一数组中的每个元素都必须是属于同一类型。
一维数组
定义
定义一维数组的形式为:类型名 数组名[常量表达式];
比如:int a[10]; 表示数组名为a,此数组为整型,有10个元素,换句话说是有10个元素的整型数组。
说明:
1、数组名的定名规则和变量名相同,遵循标识符规则
2、用方括号扩起来的常量表达式表示下标值,常量表达式可以包括常量、常变量和符号常量,但是不能包括变量。如:下面的写法是合理的
int a[10];
int a[2*5];
int a[n*2]; // n在之前已经定义为常量
3、常量表达式的值表示数组元素的个数,即数组的长度。例如:在int a[10];中,10表示数组a有10个元素,下标从0开始,注意最后一个元素是a[9]而不是a[10];
引用一维数组
数组必须先定义,然后使用。只能逐个引用数组元素的值而不能一次引用整个数组中的全部元素的值。数组元素的表示形式:数组名[下标],例如:
a[0] = a[5] + a[7] - a[6];
例子:定义一个整型数组a,把0-9共10个整数赋给数组元素a[0]-a[9],然后逆序输出
#include<iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int i,a[10];
for (int i = 0; i < 10; i++)
{
a[i] = i;
}
for (int i = 9; i >= 0; i--)
{
cout<<a[i]<<endl;
}
return 0;
}
一维数组的初始化
1、在定义数组时对全部数组元素赋值
int a[10] = {0,1,2,3,4,5,6,7,8,9};
将数组元素的初值依次放在花括号内,经过上面的初始化和定义之后,a[0]=0,a[1]=1,...,a[9]=9;
2、可以只给部分元素赋值
int a[10] = {0,1,2,3,4};
定义数组a有10个元素,但是初始值为5个,这表示只给前面5个元素赋初值,后面的五个元素值默认为0
3、在对数组元素赋初值的时候可以不指定数组长度
int a[5] = {0,1,2,3,4};
可以写成
int a[] = {0,1,2,3,4};
第二种写法,花括号内有5个元素,系统会自动定义数组a的长度为5。
例子:用数组来处理Fibonacci数列问题
#include<iostream>
#include<iomanip>
using namespace std;
int main(int argc, char const *argv[])
{
int i;
int f[20] = {1,1}; //f[0] = 1, f[1] = 1
for (int i = 2; i < 20; ++i)
{
f[i] = f[i-2] + f[i-1]; // 在i=2时,f[2]=f[0]+f[1],其余类推
}
for (int i = 0; i < 20; ++i) // 输出20个数
{
if (i%5 == 0) cout << endl; // 控制换行,每行输出5个
cout<<setw(8)<<f[i]; // 每个数据输出时占8列宽度
}
cout<<endl;
return 0;
}
结果:
1 1 2 3 5
8 13 21 34 55
89 144 233 377 610
987 1597 2584 4181 6765
二维数组
定义二维数组
具有两个下标的数组称为二维数组,定义二维数组的一般形式:类型名 数组名[常量表达式][常量表达式]
例如:
float a[3][4],b[5][10];
定义a为3x4(3行4列)的单精度数组,b为5x10(5行10列)的单精度数组。我们可以把二维数组看作一种特殊的一维数组,它的元素又是一个一维数组。例如:可以把a看作是一个一维数组,它又3个元素:a[0],a[1],a[2],每个元素又是一个包含4个元素的一维数组。
C++中二维数组中元素的排列顺序是:按行存放,即在内存中先顺序存放第一行的元素,在存放第二行的元素。如下图:
C++中允许使用多维数组,如:定义三维数组
float a[2][3][4];
定义float型三维数组a,它有2x3x4=24个元素。
引用二维数组
引用二维数组的元素一般形式为:数组名 [下标][下标],如 a[2][3],数组元素是左值,可以出现在表达式中,也可以被赋值,例如:
b[1][2] = a[2][3]/2;
在使用数组元素时,应该注意下标值在已定义的数组范围内,即不要越界使用数组。如:
int a[3][4]; // 定义数组
...
a[3][4] = 15; // 引用数组错误
上面很明显就是错误的,数组a的最大行下标为2,最大列下标为3,最多使用a[2][3]。
二维数组初始化
1、按行给二维数组全部元素赋初值
int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
该方法比较直观,第一个花括号内的数据给第一行的元素,第二个花括号内的数据给第二行的元素,按行赋值。
2、可以将所有数据写在一个花括号内,按数组排列的顺序对全部元素赋初值
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
3、可以对部分元素赋初值
int a[3][4] = {{1},{3},{4}};
上面只对各行第一列元素赋值,其余自动置为0,如下:
1 0 0 0
3 0 0 0
4 0 0 0
也可以只对几行赋值
int a[3][4] = {{1,2},{7}};
初始化后数组元素如下:
1 2 0 0
7 0 0 0
0 0 0 0
4、如果对全部元素都赋初值,则定义数组时对第一维的长度可以不指定,但是第2维的长度不能省略
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
可以改写为:
int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
系统会根据数据总个数分配储存空间,一共12个数据,每行4列,所以可以确定为3行
例子:将一个2x3的二维数组a的行和列元素互换,存储到一个3x2的二维数组中。如:
a = {{1,2,3},{4,5,6}}
转换后为b={{1,4},{2,5},{3,6}}
int main(int argc, char const *argv[])
{
int a[2][3] = {{1,2,3},{4,5,6}};
int b[3][3],i,j;
cout<<"array a: "<<endl;
for (i = 0; i <=1; i++)
{
for (j = 0; j <=2; j++)
{
cout<<a[i][j]<<" ";
b[j][i] = a[i][j]; // 交换位置
}
cout<<endl;
}
cout<<"array b: "<<endl;
for (i = 0; i <=1; i++)
{
for (j = 0; j <=2; j++)
{
cout<<b[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
效果:
array a:
1 2 3
4 5 6
array b:
1 4 0
2 5 1
数组做函数参数
数组名可以做实参和形参,传递的是数组的起始地址。此时实参和形参都应该使用数组名(也可以使用指针)。
例子:用选择法对数组中10个整数进行由小到大的排序。
选择法:先将10个数中最小的数与a[0]对换,再将a[1]-a[9]中最小的数与a[1]对换,依次下去,每比较一轮,找出未排序数中的最小值并进行交互。
#include<iostream>
#include<iomanip>
using namespace std;
int main(int argc, char const *argv[])
{
void selectsSort(int array[], int n); //函数声明
int a[10] = {3,8,7,9,1,2,6,4,0,5}; // 初始数组
selectsSort(a,10); // 排序
for (int i = 0; i < 10; ++i) // 输出排序后的数组
{
cout<<a[i]<<endl;
}
}
void selectsSort(int array[], int n)
{
int i,j,k,t;
for (i = 0; i < n - 1; i++)
{
k=i;
for (j = i+1; j < n; j++)
if (array[j] < array[k]) k=j; // 找到最小位置
// 交换值
t = array[k];
array[k] = array[i];
array[i] = t;
}
}
数组名做参数注意:
1、如果函数实参是数组名,形参也应该是数组名或者指针变量,实参数组和形参数组数据类型应该一致。
2、数组名代表数组首元素的地址,并不代表数组中的全部元素。因此用数组名做函数实参时,不是把实参数组各元素的值传递给形参,而只是将实参数组首元素地址传递给形参。并且实参数组和形参数组共占同一内存单元。比如:数组a的起始地址是1000,那么数组b的起始地址也是1000
3、在使用变量做函数参数时,只能将实参变量的值传递给形参变量,在调用函数的过程中如果改变了形参的值,对实参没有影响。即实参的值不因形参的改变而改变。但是使用数组名作为实参时,如果改变了形参数组元素的值将同时改变实参数组元素的值
字符数组
顾名思义,字符数组就是用来存放字符数据的数组,字符数组中的一个元素存放一个字符。
定义和初始化字符数组
char a[4] = {'l','o','n','g'}; // 定义并初始化 ok
注意:不能对整个数组一次赋值:
char a[4];
a = {'l','o','n','g'}; // 错误
需要单独赋值;
char a[4];
a[0] = 'l';
a[1] = 'o';
a[2] = 'n';
a[3] = 'g';
也可以直接使用字符串初始化:
char c[5] = "long";
字符串和字符串结束标识
使用字符数组可以存放一个字符串中的字符。例如:
char str[12] = {'I',' ','a','m',' ','h','a','p','p','y'};
上面代码使用了一维数组str存放一个字符串"I am happy"中的字符,这个字符的实际长度(10)与数组长度(12)并不相等,在存放上面10个字符之外,系统对字符数组最后两个元素自动填充空字符'\0'(注意:不是空格字符)。'\0'是字符串结束标志,由它前面的字符组成有效字符。"I am happy"中共有10个字符,但在内存中占11个字节,因为最后的'\0'是由系统自动加上的。用它做字符串结束标识不会产生任何附加的操作,只是起一个标识作用。
字符数组的输入输出
字符数组的输入输出由两种方法:
1、逐个字符输入输出
2、将整个字符串一次输入输出,如:
char str[5];
cin>>str; // 使用字符数组名输入字符串
cout<<str; // 使用字符数组名输出字符串
比如:输入long,输出long
注意几点:
1、输出的字符不包括结束符'\0'
2、输出字符串时,cout流中用字符数组名,而不是数组元素名。
3、如果数组长度大于字符串实际长度,输出遇到'\0'结束
4、如果一个字符数组包含一个以上'\0',则遇到第一个'\0'时输出结束
5、用cin从键盘向计算机输入一个字符串时,从键盘输入的字符串应该短于已定义的字符数组的长度,否则会出现问题