基本介绍
数组(Array):是多个相同的类型数据按照一定的顺序排列集合,并使用一个名字命名,通过编号的方式对这些数据进行统一的管理。
数组中的概念
数组的特点:
- 数组中的元素在内存中是依次紧密排列;
- 创建数组对象会在内存中创建一块连续的的空间。占据空间的大小,取决于数组的长度和数组中元素的类型;
- 通过下标直接调用指定位置的元素;
- 数组,一旦初始化完成,长度确定。数组的长度一旦确定,就不能修改;
数组的分类
1.按照数组的维度分:
| 2.按照元素的数据类型分:
|
一维数组
1.定义方式
方式一:
int array[10]//数组名array,里面有十个元素,每个元素都是int类型。
方式二:(宏定义的方式)
#define num 10
int array[num];
2.数组元素的调用(赋值,取出)
格式:数组名[下标];
arr[0] = 13; //对该位置数组元素进行赋值
int score = arr[0]; //调用此位置的元素值
注意:数组的下标从0开始,最大下标是数组长度-1。
数组角标的越界:
例:
int scores[20];
scores[20] = 51;
3.数组的长度
字节长度:sizeof运算符会返回整个数组的字节长度
数组长度:数组整体的字节长度 / 数组元素的字节长度;
计算方式:(如下代码)
int array[10];
size_t array_length = sizeof(array)/sizeof(array[10]);
复习:sizeof 返回值的数据类型是 size_t ,所以 sizeof(array) / sizeof(array[10]) 的数据类型也是size_t 。在 printf() 里面的占位符,要用%zd 或%zu 。
注意:数组一旦声明/定义了,其长度就固定了,不能动态变化。
4.数组的遍历
将数组中的每个元素分别获取出来,就是 遍历 。一般与for循环结合。
例:声明长度为10的int类型数组,给数组元素依次赋值为 0,1,2,3,4,5,6,7,8,9,并遍历数组所有元素。
法一:赋值(未使用sizeof运算符)
int number[10];
for (int i = 0; i < 10; ++i) {
number[i] = i;
printf("%d\t",number[i]);
}
法二:赋值加遍历(使用sizeof运算符)
size_t array_bits = sizeof(number);//数组的字节长度
size_t length =sizeof (int);//数据类型占用的字节,元素的字节
//给数组中元素赋值
for (int i = 0; i < array_bits/length; ++i) {
number[i] = i;
}
//遍历所赋值的数组中的元素
for (int i = 0; i < array_bits/length; ++i) {
printf("%d",number[i]);
}
5.数组的其他定义方式
方式一:
- 数组在声明时,使用大括号,同时对每一个成员赋值
int arr[3] = {10,20,30};
// 等同于
int arr[] = {10,20,30}; //数组 arr 的长度,将根据大括号里
面的值的数量,确定为 3
-
对数组部分元素赋初值:如果大括号里面的值,少于数组的成员数量,那么未赋值的成员自动初始化为 0 。
int arr[5] = {10, 20, 30};
// 等同于
int arr[5] = {10,20,30, 0, 0};
注意:大括号里面的值只能少于或等于元素的数量,不能超过。 否则编译会报错。
-
将整个数组的 每一个成员都设置为零 ,最简单的方式如下
int a[100] = {0};
//其他类型赋值
double b[10] = {0.0};
char c[10] = {0};
方式二:
- 指定数组下标位置上的元素进行赋值。
int a[10] = {[1] = 1,[2] = 3};
//未指定的索引位置自动赋值为0
-
指定位置的赋值与顺序赋值,可以结合使用。
int arr[15] = {1, [5] = 10, 11, [10] = 20, 21}; //角标0、
5、6、10、11的位置被赋值
- 省略成员数量时,如果同时采用指定位置的赋值,那么数组长度将是最大的指定位置再加1。
int arr[] = {[2] = 6, [9] = 12}; //此时数组的长度是10
6. 一维数组的内存分析
针对如下代码:
int a[5] = {1,2,3,4,5};
对应的内存结构:
说明:
- 数组名a的地址,记录该数组的首地址,即a[0]的地址;
- 由于数组的各元素是连续分布的,假如a[0]的地址是0x1122,则a[1]地址= a[0]的地址+int字节数(4) = 0x1122 + 4 = 0x1126,后面依次类推。
注意事项:
C 语言规定,数组变量一旦声明,数组名指向的地址就不可更改。
一些错误的写法
1.声明数组后,再用大括号赋值
int nums[5];
nums = {22, 37, 3490, 18, 95}; // 使用大括号赋值时,必须在
数组声明时赋值,否则编译时会报错。
2.不能将一个数组名赋值给另一个数组名
int a[5] = {1, 2, 3, 4, 5};
// 写法一
int b[5] = a; // 报错
// 写法二
int b[5];
b = a; // 报错
7.变长数组
定义:数组声明的时候,数组长度除了使用常量,也可以使用变量或表达式来指定数组的大小。这叫做 变长数组。
注意:变长数组在C99标准中被引入,某些编译器可能不支持变长数组,或者可能有特定的限制和行为。
方式1:
int n = 10;
int arr[n];
int i = 10;
int a1[i];
int a2[i + 5];
int a3[i + k];
方式2:(某些编译器版本不支持变长数组)
采用malloc()函数来创建动态大小的数组,采用指针的形式(通用方式)。
方法:int *arr:int类型的指针 = (int*:强转成int类型的指针变量)malloc(数组的字节长度:数组长度*单个元素的占用的字节大小)————在堆空间开辟的数组空间
- 分配:
int length = 5;
int *arr = (int *)malloc(length * sizeof(int));
arr[0] = 10;
arr[1] = 20;
printf("%d\n",arr[1]);
//注意:使用完动态创建的数组后,一定要回收此数组的内存空间,利用free()函数。否则就会内存泄漏(不使用且未回收的内存空间)。
-
释放:
free(arr);
一维数组的应用
1.数值型数组特征值统计
特征值:平均值、最大值、最小值、总和等
例题:
1.定义一个int型的一维数组,包含10个元素,然后求出数组中的最大值,最小值,总和,平均值,并输出出来。
#include <stdio.h>
int main() {
//定义一个int类型的一维数组,包含十个元素,然后求出数组中的最大值、最小值、总和、平均值,并输出出来。
int num[10] = {2, 4, 6, 8, 9, 5, 4, 1, 2};
size_t num_length = sizeof(num) / sizeof(int);//数组的长度
//*****数组中的最大值*****
int max = num[0];//假设数组第一个元素最大
for (int i = 0; i < num_length; ++i) {
if (max < num[i]) {
max = num[i];
}
}
printf("max:%d\n", max);
//*****数组中的最小值*****
int min = num[0];
for (int i = 0; i < num_length; ++i) {
if (min > num[i]) {
min = num[i];
}
}
printf("min:%d\n", min);
//*****总和*****
int sum = 0;//注意:sum一定要进行初始化,要不然运行结果会出错
for (int i = 0; i < num_length; ++i) {
sum += num[i];
}
printf("sum:%d\n", sum);
//*****平均数*****
//思路:求数组的总和以及数组的长度
double average = (double) sum / num_length;
printf("average:%.2lf", average);
return 0;
}
2.在编程竞赛中,有10位评委为参赛的选手打分,分数分别为:5,4,6,8,9,0,1,2,7, 3。求选手的最后得分(去掉一个最高分和一个最低分后其余8位评委打分的平均值)
思路:依次求出数组中的最大值、最小值和总和,最后求平均值
int main() {
int scores[10] = {5,4,6,8,9,0,1,2,7,3};
int max = scores[0]; //记录最高分
int min = scores[0]; //记录最低分
int sum = 0; //记录总分
int arrLen = sizeof(scores) / sizeof(int); //记录数组
长度
for(int i = 0;i < arrLen;i++){
if(max < scores[i]){
max = scores[i];
}
if(min > scores[i]){
min = scores[i];
}
sum += scores[i];
}
//计算平均分
double avg = (double)(sum - max - min) / (arrLen -
2);
printf("选手去掉最高分和最低分之后的平均分为:%.2lf\n" ,
avg);
return 0;
}
2.数组的复制
错误的方式:
int a[3] = {1,2,3};
int *b = a;//定义一个指针变量,将数组名赋值
b[0] = 2;
for (int i = 0; i < 3; ++i) {
printf("%d ",a[i]);
}
上述方式:改变复制后的b数组,会将源数组a的元素也改变,故不能叫做复制数组
正确的方式:
方式1:使用循环
#include <stdio.h>
#define length 3
int main(){
int a1[length] = {1,2,3};
int a2[length];//定义一个与源数组长度,数值类型相同的元素
for (int i = 0; i < length; ++i) {
a2[i] = a1[i];
printf("%d ",a2[i]);
return 0;
}
方式2:使用memcpy()函数
#include <stdio.h>
#include <string.h>
#define LENGTH 3
int main() {
int a[LENGTH] = {10, 20, 30};
int b[LENGTH];
// 使用 memcpy 函数复制数组 a 到数组 b
memcpy(b, a, LENGTH * sizeof(int)); //使用alt键+回车键快速添加头文件
// 打印数组 b 的内容
printf("复制后的数组 b:");
for (int i = 0; i < LENGTH; i++) {
printf("%d ", b[i]);
}
printf("\n");
return 0;
}
3.数组元素的反转
方式1:(将数组arr[i]与arr[length - 1 - i]的位置互换)
int array[10]={1,2,3,4,5,6,7,8,9,10};
//获取数组的长度
size_t arr_l = sizeof (array)/sizeof (int);
//进行数组位置的互换
int tempt;
for (int i = 0; i < arr_l/2; ++i) { //注意,循环条件要减半
tempt = array[i];
array[i] = array[arr_l-1-i];
array[arr_l - 1 -i] = tempt;
}
//遍历数组array中的元素
for (int i = 0; i < arr_l; ++i) {
printf("%d ",array[i]);
}
//换行
printf("\n");
方式2:(将数组分成两部分,二者进行互换)——两个判断条件
int array[10]={1,2,3,4,5,6,7,8,9,10};
//获取数组的长度
size_t arr_l = sizeof (array)/sizeof (int);
//将数组分成左右两部分进行互换
for (int left = 0,right = arr_l - 1; left < right; left++,right --) {
int temp = array[left];
array[left] = array[right];
array[right] = temp;
}
//遍历数组array中的元素
for (int i = 0; i < arr_l; ++i) {
printf("%d ",array[i]);
}
//换行
printf("\n");
上述代码,可以改成利用while循环。
char型数组与字符串(难点)
char型数组:字符型数组,顾名思义,数组元素的数据类型为字符型的数组。
char arr[3] = {'a','b','c'};
其中,字符数组可以存储字符串。
1.字符串的使用
C语言没有专门用于存储字符串的变量类型,字符串都被存储在char类型的数组中。在字符串结尾,C 语言会自动添加一个 '\0' 的转义字符作为字符串结束的标志,所以字符数组也必须以'\0'字符结束。
char str1[5] = {'a','b','c','d','\0'};//在末尾加上'\0'
for (int i = 0; i < 5; ++i) {
printf("%c",str1[i]);
}
写法2:(简化方法:利用双引号“”)
char str2[12] = {"hello world"};//注意使用双引号,非单引号
char str3[12] = "hello world"; //可以省略一对{}来初始化数组元素
2.字符串长度和字符串数组长度
字符串长度
#include <stdio.h>
#include <string.h> //需要加载此头文件
int main() {
char nation[10] = "China";
printf("%d\n", strlen(nation)); //5
}
字符串长度:可用 <string.h>文件下的strlen()函数来计算。
字符串数组的长度
字符串数组包括 ‘\0’字符 ,因此字符串数组要比字符串长度要长。
char str3[12] = "hello world";
对于上面代码,字符串长度:11;而字符串数组的长度:12。
练习:输出字符数组
#include <stdio.h>
int main() {
char str1[]={"China\nBeijing"};
char str2[] = "helloworld";
puts(str1);//输出字符串
puts(str2);
return 0;
}
多维数组(二维)
1.定义方式1
格式:数组名【下标】【下标】
int a[3][4];
理解:
从逻辑上的理解:
- 对于二维数组的理解,可以看作是由一维数组嵌套而成的。
-
二维数组,常称为 矩阵 (matrix) 。把二维数组写成 行 (row) 和 列(column) 的排列形式。
从内存方面理解
-
而在 内存 中,各元素是连续存放的,不是二维的,是 线性 的。
-
二维数组中元素排列的顺序是 按行存放 的。即:先顺序存放第一行的元素,再存放第二行的元素。
例如:b[3][3]在内存上的存储
关于长度:
成员的调用(赋值,遍历)
举例1:给指定索引位置的元素赋值
int a[3][4];
a[0][1] = 1;
a[1][1] = 2;
举例2:遍历二维数组的元素,并查看数组中元素的地址。
#include <stdio.h>
int main(){
//定义一个二维数组,进行赋值,并遍历数组的元素和地址
int arr[3][4];
//******给二维数组赋值*****
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
arr[i][j] = i+j;
}
}
//*****遍历元素和地址*****
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
printf("%d ",arr[i][j]);//遍历元素
printf("arr[%d][%d] = %p \n",i,j,&arr[i][j]);
}
}
printf("\n");
return 0;
}
2.二维数组的其他定义方式
方式1:声明与初始化同时进行
多维数组也可以使用大括号,在声明的同时,一次性对所有成员赋值。
int a[3][4] = {{1,2,3,4},
{5,6,7,8},
{9,10,11,12}};
方式2:部分元素赋值
//指定了 [0][0] 和 [1][1] 位置的值,其他位置就自动设为 0 。
int a[2][2] = {[0][0] = 1, [1][1] = 2};
方式3:使用单层大括号赋值
int a[2][2] = {1, 0, 0, 2}; //会自动匹配到各行各列
进一步对方式4简化:
//int a[2][3] = {1, 2, 3, 4, 5, 6};
//可以写为:
int a[][3] = {1, 2, 3, 4, 5, 6};
//也可以写为:
int a[][3] = {{1, 2, 3},{4, 5, 6}}; //行数自然判定为2
注意:一定要给定列数,而不能给定行数。
例:错误的方式
int array[][]; //错误,必须指定列数
int array[3][]; //错误,必须指定列数
3.二维数组的举例
举例1:获取arr数组中所有元素的和
举例2: 求二维数组最大值以及对应的行列角标
举例3:将一个二维数组行和列的元素互换,存到另一个二维数组中。
举例4: 二维char型数组
将''apple''、"orange"、“grape”、“pear”、“peach”存在数组中
举例5:打印一个10行杨辉三角。
提示:
1. 第一行有 1 个元素, 第 n 行有 n 个元素
2. 每一行的第一个元素和最后一个元素都是 1
3. 从第三行开始, 对于非第一个元素和最后一个元素的元素。
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];