一、数组的概念
数组:是多个相同类型数据,按照一定顺序排列的集合,并使用一个名字命名,通过编号方式对这些数据进行统一管理。
1.1数组的几个概念
1.数组名 2.下标 3.元素 4.数组的长度
1.数组是C语言中的一种数据结构,用于存储一组具有相同数据类型的数据
2.数组中的每个元素可以通过一个索引(下标)来访问,索引从0开始,最大值为数组长度减一。
3.数组是若干个相同类型的变量在内存中有序存储的集合。
1.2数组的特点
1.数组中的元素在内存中是依次紧密排列的,有序的。
2.创建数组对象会在内存中开辟一整块连续的空间。占据的空间大小,取决于数组的长度和数组中的元素的类型。
3.我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
4.数组,一旦初始化完成,其长度就是确定的,数组的长度一旦确定,就不能修改。
5.数组名中引用的是这块连续空间的首地址。
注意!!!int型每个元素占四个字节,如果知道第一个元素的地址,就可以计算出后面元素地址(因为空间是连续的)
1.3数组的分类
1.3.1按元素类型分类:
(1)字符数组:即若干个字符变量的集合,数组中每个元素都是字符型的变量
char s[10]; s[0],s[1]......s[9]
(2)短整型的数组:short int a[10]
(3)整型的数组:int a[10];
(4)长整型的数组:lont int a[5];
(5)浮点型的数组(单、双):float a[6]; a[4]=3.14f; doubule a[8]; a[7]=3.1415926;
(6)指针数组:char *[10]; int *a[10];
(7)结构体数组:struch stu boy[10];
1.3.2、按维数(度)分类
一维数组:存储一组数据
二维数组: 存储多组数据,相当于二维表,一行代表一组数据。每一行长度可以不同
三维数组、四维数组等。。。。
二、一维数组定义
2.1数组的定义方式
数组通过变量名后加方括号表示,方括号里面是数组可以容纳的成员数量(即长度)
定义数组的语法格式:类型 数组名[元素个数];
int arr[10];//数组arr,里面包含10个成员,每个成员都是int类型
或者
#define NUM 10
int arr1[NUM];
注意:声明数组时,必须给出数组大小。
1.数组名不能与其他变量名相同,同一作用域内时唯一的
2.其下标从0开始,因此5个元素分别为arr[0] arr[1] arr[2] arr[3] arr[4]
2.2数组元素的调用(增、改及取元素)
格式:数组名【下标】
数组的下标从0开始,用int arr[10]定义数组,则最大下标值为9,不存在数组元素arr[10]
arr[0]=13;对该位置数组元素进行赋值
int score=arr[0];//调用此位置的元素的值
数组角标越界:
假设数组有n个元素,如果使用的数组的下标小于0,或者大于n-1,就是数组越界访问,超出了数组合法空间访问。
C语言不做数组下标越界的检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确。
int arr[20];
arr[20]=51;
2.3关于长度
数组的字节长度
sizeof运算符会返回整个数组的字节长度。
int arr[10];
printf("数组的字节长度为:%zd\n",sizeof(arr));
数组的长度
在定义数组时,需要指定数组中元素的个数,方括号中的常量表达式用来表达元素的个数,即数组长度。
由于数组成员都是同一个类型,每个成员的字节长度都是一样的,所以数组整体的字节长度除以某个数组元素的字节长度,就可以得到数组的成员数量。
//数组中元素的个数
int arrLen=sizeof(arr)/sizeof(arr[0]);
或
int arr[10];
int arrLen=sizeof(arr)/sizeof(int);
注意!!!sizeof返回值的数据类型是size_t,所以sizeof(a)/sizeof(a[0])的类型也是size_t,在printf()里的占位符,要用%zd或%zu. %lld也行。
#include<stdio.h>
//定义一个数组
#define NUM 20
int main() {
int arr[10];
int arr1[NUM];
//2.数组元素的调用
//下标0 到数组长度-1
arr[0] = 10;
arr[1] = 20;
//3.问题:数组角标越界
//int arr[10] = 100; //该行错误
//printf("%d\n", arr2[5]); //没必要打印 越界
//4.关于长度
//数组占用字节数
int arr3[20];
printf("数组的长度:%zu\n", sizeof(arr3) / sizeof(arr3[0]));
double arr4[20];
printf("数组的长度:%zu\n", sizeof(arr4) / sizeof(arr4[0]));
//数组长度
printf("数组的长度:%zd\n", sizeof(arr3) / sizeof(int));
printf("数组的长度:%zd\n", sizeof(arr4) / sizeof(double));
return 0;
}
2.4 数据的遍历
将数组中的每个元素分别获取出来,就是遍历。for循环与数组遍历是绝配。
需求:声明长度为10的int类型数组,给数组元素依次赋值为0,1,2,3,4,5,6,7,8,9并遍历数组中所有的元素
#include<stdio.h>
int main() {
int arr[10];
for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
arr[i] = i;
}printf("遍历数组中的元素:\n");
for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
printf("%d",arr[i]);
}printf("\n");
return 0;
}
2.5数组定义方式2-数组的初始化
数组可以在声明时,使用大括号,同时对每个成员赋值
1.全局数组:若不初始化,编译器将其初始化为零
2.局部数组:若不初始化,内容为随机值
int arr[5]={10,20,30,40,50}
//等同于
int arr[]={10,20,30,40,50}
注意:int arr[3]={1,2,3,4};//报错
三、一级数组的内存分析
3.1数组内存图
int a[5]={1,2,3,4,5};
该代码对应的内存结构:
说明:数组名,记录数组的首地址,即a[0]的地址
数组的各个元素是连续分布的,假如a[0]地址是0x1122,则a[1]的地址=a[0]的地址+int字节数(4)=0x1126,后面依次类推
3.2注意事项
C语言规则,数组变量一旦声明,数组名指向的地址就不可更改。因为声明数组时,编译器会自动为数组分配内存地址,这个地址与数组名是绑定的,不可更改。
因此,当数组定义后,在用大括号重新赋值,是不允许的。
3.3变长数组
变长数组的根本特征是数组长度只有运行时才能确定。他的好处是程序员不必在开发时,随意为数组指定一个估计的长度,程序员可以运行时为数组分配精确的长度。
动态内存分配:使用malloc、callor或realloc等函数来动态地分配和重新分配内存。
这运行你在运行时改变内存块(可以看作是一个动态数组)的大小。
int length=10;
int arr7[length];
int arr8[length+5];
四、 一维数组的应用
4.1数值型数组特征统计
这里的特征涉及到:平均值、最大值、最小值、总和等
例:
int main(){
int arr[5]={20,10,30,8,9};
int max=arr[0];// 用于记录数组的最大值
int arrLen=sizeof(arr)/sizeof(int);// 获取数组中元素的个数
for(int i=1;i<arrLen;i++){
if(max<arr[i]){
max=arr[i];
}
}
printf("最大值为:%d\n",max);
}
4.2数组的复制
由于数组名是指针,所以复制数组不能简单地复制数组名。
int a[3]={10,20,30};
int *b;
b=a;
上面的写法,结果不是将数组a复制给数组b,而是让a和b指向同一个数组。
正确的方式
- 循环的方法
- 用memcpy函数
#include <stdio.h>
#include <stdint.h>
//#include <string.h>
#define LENGTH 3
int main() {
int a[LENGTH] = { 10,20,30 };
int b[LENGTH];
//复制数组a到数组b
/*for (int i = 0; i < LENGTH; i++) {
b[i] = a[i];
}*/
//使用memcpy函数复制数组a到数组b;
memcpy(b, a, LENGTH * sizeof(int));
//打印数组b的内容
for (int i = 0; i < LENGTH; i++) {
printf("%d ", b[i]);
}
printf("\n");
return 0;
}
两种方法的对比
1. 循环复制:
优点: 简单直观,容易理解和实现。不需要引入额外的头文件。
缺点:需要编写循环代码来遍历数组并逐个赋值,相对而言可能稍显繁琐。不适用于复制大型数组或复杂数据结构。
2.memery函数复制:
优点:使用标准库提供的函数,可以实现快速且高效的内存复制。适用于大型数组或复杂的数据结构的复制。可以直接复制字节数,不需要遍历数组。
缺点:需要包含<string.h>头文件,对于简单的数组复制,可能有些过于繁重。
4.3数组作为函数参数进行传递
数组作为参数的注意事项:
1.数组名作为函数定义处作为接收参数的数组类型形参既可以指定数组长度,也可以不指定数组长度
2.数组元素作为函数实参传递时,数组元素类型必须和形参数据类型一致
4.4一维数组常操作的综合代码
#include<stdio.h>
//定义一个函数,让数组逆置
int getReverse(int a[],int length) {
int i = 0; // 首元素下标
int j =length - 1; // 尾元素下标
int temp;
while (i < j) {
// 元素交换值
temp = a[i];
a[i] = a[j];
a[j] = temp;
// 位置移动
i++;
j--;
}
for (i = 0; i < length; i++) {
printf("%d, ", a[i]);
}
return 0;
}
//定义一个函数,对数组元素求和******
int getSum(int a[5]) {
int sum = 0;//用来存和
for (int i = 0; i < 5; i++) {
sum += a[i];
}
return sum;
}
//定义一个数组,用来求数组最大值
//函数封装的时候,如果不指定数组元素的个数,需要传递第2个参数,表示数组的长度*******
int getMax(int arr[],int length) {
int max = 0;//用来存储最大值
for (int i = 0; i < length; i++) {
if (arr[i] >= max ) {
max = arr[i];
}
}
return max;
}
//定义一个函数,将数组元素输出
void temp(value) {
printf("%d\n", value);
}
int main() {
//数组作为函数的参数进行传递:
//案例1、封装个函数,进行数组求和
int arr[5] = { 1,200,3,4,5 };
getSum(arr);
printf("数组元素的和是:%d\n", getSum(arr));
//案例2、定义一个函数,求已有数组的最大值
int length=sizeof(arr)/sizeof(arr[0]);
printf("数组中最大值为:%d\n", getMax(arr,length));
//案例3、定义一个函数,求数组逆置
int len=sizeof(arr)/sizeof(srr[0]);
getReverse(arr,len);
//案例4、数组元素也可以作为函数参数进行传递
temp(arr[1]);
return 0;
}
五、二维数组
5.1二维数组的定义
多维数组的定义格式是:
数据类型 数组名称[常量表达式1][常量表达式2]......[常量表达式n]
二维数组的定义方式1:
int arr[3][4];
arr[0][0]=1;
arr[0][1]=2;
二维数组的定义方式2:
int a[3][4]= {{1,2,34},5,6,7,8],{9,10,11,12}}
关于长度
int b[3][3];
printf("%lld\n",sizeof(b))//36
printf("%lld\n",sizeof(b)/sizeof(int));//9
5.2操作二维数组
#include <stdio.h>
int main() {
/*
二维数组 或多维数组的定义格式是:
数据类型 数组名称[常量表达式1][常量表达式2]...[常量表达式n];
*/
int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
//arr 数组名称 第一个[3]表示一维数组的下标,表示长度
// 第二个[3] 表示二维数组的下标,表示长度
printf("%d\n", arr[0][0]);//1
printf("%d'\n", arr[1][2]);//6
printf("%d\n", arr[2][1]);//8
int menu2[2][2] = { {4},{9,0} }; //部分初始,赋值不够的会补0;
printf("%d\n", menu2[1][1]);
//------二维数组的遍历
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2; j++) {
printf("%d\n", arr[i][j]);
}
}
//获取各个数组元素的地址:
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2; j++) {
printf("arr[%d][%d]%p\n",i,j, &arr[i][j]);
}
}
//求二维数组的行数和列数
printf("%d\n", sizeof(arr));//二维数组所占内存字节数 36
printf("%d\n", sizeof(arr[0]));//一维数组所占内存字节数 12
printf("%d\n", sizeof(arr[0][0]));//一个元素所占的字节数4
printf("%d\n", sizeof(arr) / sizeof(arr[0]));//行数 3
printf("%d\n", sizeof(arr[0]) / sizeof(arr[0][0]));//列数 3
return 0;
}
六、char型数组和字符串
字符数组与字符串的区别
- C语言中没有字符串这种数据类型,可以通过char的数组来替代
- 以数字0 (和字符 '\0' 等价)结尾的char数组就是一个字符串,字符串是一种特殊的char的数组
字符型数组,顾名思义,数组元素的数据类型为字符型的数组
一方面,可以看做普通数组,初始化、常用操作如前所述。
char arr[]={'a','b','c','d'};