数组概念
数组是一组相同类型元素的集合;
- 数组可以存放1个或多个数据,但是数据元素个数不能为0;
- 数组中存放的多个数据,类型必须相同;
数组分为一维数组和多维数组,多维数组中常见的是二维数组。
一维数组的创建与初始化
type arr_name[常量值1];
基本语法:
数据类型 数组名[数组大小];
如:
int numbers[10]; // 10 个 int 类型的数组
float prices[5]; // 5 个 float 类型的数组
char name[20]; // 20 个 char 类型的数组(字符串)
数组的初始化
1.定义时初始化
int arr1[5] = {85, 90, 78, 92, 88};
2.部分初始化
int arr1[5] = {85, 90};
// 结果:{85, 90, 0, 0, 0}
3.全部初始化为0
int arr1[5] = {85, 90};
// 结果:{85, 90, 0, 0, 0}
4.不指定大小
int arr1[] = {85, 90, 78, 92, 88};
// 编译器自动算出大小是 5
数组的类型
- int是数组元素的类型;
- int []是数组的类型;
一维数组的使用
数组的访问
数组使用下标(索引)访问,从0开始;
最大的下标是数组的大小减去1;
int arr1[5] = {85, 90, 78, 92, 88}
索引: [0] [1] [2] [3] [4]
值: 85 90 78 92 88
数组的输入与元素打印
//用户输入1—10;随后遍历输出 #include<stdio.h> int main(){ int arr1[10]; for (int i = 0; i < 10; i++) { printf("输入数字\n"); scanf("%d",&arr1[i]); } for ( int r = 0; r < 10; r++) { printf("%d\n",arr1[r]); } return 0; }一维数组在内存中的储存
使用%p来查看地址后发现从低到高排列了数组中每个数据的地址,而差值刚好是4,因为int是数组元素的类型,而int类型占的内存为4字节。
sizeof计算数组元素个数
sizeof是c语言中的一个操作符(并不是函数),用来计算某个类型或变量在内存中占用的字节数。
- 注意:
%zu是专门打印size_t类型(sizeof的返回值类型)的格式符。
由于int类型占用字节为4,所以40/4=10,元素个数为10;
char: 1 字节
int: 4 字节
float: 4 字节
double: 8 字节
二维数组的创建
二维数组的概念
前面学习的数组被称为一维数组,数组的元素都是内置类型的,如果我们把一维数组做为数组的元素,这时候就是二维数组,二维数组作为数组元素的数组被称为三维数组,二维数组以上的数组统称为多维数组;
简成一句话就是:一维装数据,二维装一维,三维装二维,更高维就是继续套娃;
二维数组的创建
type arr_name [常量值1][常量值2];
也就是 数据类型 数组名 [行数] [列数];
int arr1_int [3] [4]; // 3行4列的整数矩阵
float arr1_float [5] [3]; // 5个学生,每人3科成绩
char arr1_char [8] [8]; // 8x8 的棋盘(国际象棋)
二维数组的初始化
1.完全初始化
int arr1 [3] [4] =
{
{1,2,3,4},
{11,12,13,14},
{21,22,23,24}
};
2.完全初始化(写在一行)
int arr1 [3][4]={1,2,3,4,11,12,13,14,21,22,23,24};
效果与上面一样,但是分行看更清晰
3.部分初始化(未初始化的自动为0)
int matrix[3][4] = {
{1, 2},
{5, 6, 7},
{9}
};
4.省略行数(行数可以省略但列数坚决不能省略)
int matrix[][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 编译器自动算出行数 = 3
注意,部分初始化和完全未初始化不一样
| 部分初始化 | int a[5] = {1, 2}; | 自动为 0 | 编译器自动填充 0 |
| 完全未初始化 | int a[5]; | 垃圾值(不确定) | 只分配内存,不赋值 |
二维数组的使用
二维数组的下标
二维数组的下标与一维数组差不多,都是从0开始的
这样的话我们可以根据行数列数来具体定位到数组中的元素了。
#include<stdio.h> int main(){ int arr2[3][4] = { {1,2,3,4}, {11,12,13,14}, {21,22,23,24} }; printf("%d",arr2[1][3]); return 0 ; }定位信息为第二行第四列。输出结果14
二维数组的输入和输出
遍历二维数组
#include<stdio.h> int main(){ int arr2[3][4] = { {1,2,3,4}, {11,12,13,14}, {21,22,23,24} }; for (int i = 0; i < 3; i++) //行下标 { for (int j = 0; j < 4; j++) //列下标 { printf("%d\t",arr2[i][j]); } printf("\n"); } return 0 ; }当把其中的代码修改一下就可以进行输入了;
#include<stdio.h> int main(){ int arr2[3][4] ; for (int i = 0; i < 3; i++) //行下标 { for (int j = 0; j < 4; j++) //列下标 { int b = i + 1; int c = j + 1; printf("输入第%d行,第%d列的数据",b,c); scanf("%d",&arr2[i][j]); } printf("\n"); } for (int i = 0; i < 3; i++) //行下标 { for (int j = 0; j < 4; j++) //列下标 { printf("%d\t",arr2[i][j]); } printf("\n"); } return 0 ; }二维数组的使用及在内存中的存储
#include <stdio.h> int main() { int arr[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; printf("arr[0][0] 的地址: %p\n", &arr[0][0]); printf("arr[0][1] 的地址: %p\n", &arr[0][1]); printf("arr[0][2] 的地址: %p\n", &arr[0][2]); printf("arr[0][3] 的地址: %p\n", &arr[0][3]); printf("arr[1][0] 的地址: %p\n", &arr[1][0]); printf("arr[1][1] 的地址: %p\n", &arr[1][1]); // 计算相邻元素地址差 printf("\n地址差: %ld 字节\n", (char*)&arr[0][1] - (char*)&arr[0][0]); return 0; }可以看到并没有像二维数组的概念中的图一样换行,他们在一行。
因为逻辑视图与物理视图是不一样的
逻辑中
内存中
变长数组
下面是一个逆序输出的程序
#include <stdio.h> int main() { char arr_input[100]; printf("请输入(不超过100位):"); scanf("%[^\n]",arr_input); /*将原来的int改为字符串这样不管是数字还是字符都可以逆序输出。%[^\n]意为读取所以不是换行符的字符*/ int i = 0; while (arr_input[i] != '\0') //记录数组有多少个元素;\0意为 空字符(Null)标记字符串结束。 { i++; } printf("逆序输出:"); for ( i = i-1 ; i >= 0; i--) //记录有i组后 由于数组是由0开始计数所以需要i-1,当i>=0时进入循环打印当前数组内的数据,i--。 { printf("%c",arr_input[i]); } return 0; }假如100不够呢,用户想输入101个,200个?
这个时候可以使用变长数组
在arr_input[100]改为arr_input[n],再由用户输入想输入几位数,来自定义数组元素的个数。
这样会出现两个scanf,当前面的scanf读取完后会留下\n换行符,后面的sanf读取到换行符就没有办法正常运行了,使用getchar()消除掉第一个scanf留下的换行符,这样就可以正常运行了。
#include <stdio.h> int main() { int n = 0; printf("请输入需要逆序的位数:\n"); scanf("%d",&n); char arr_input[n]; printf("请输入(不超过100位):"); getchar(); scanf("%[^\n]",arr_input); /*将原来的int改为字符串这样不管是数字还是字符都可以逆序输出。%[^\n]意为读取所以不是换行符的字符*/ int i = 0; while (arr_input[i] != '\0') //记录数组有多少个元素;\0意为 空字符(Null)标记字符串结束。 { i++; } printf("逆序输出:"); for ( i = i-1 ; i >= 0; i--) //记录有i组后 由于数组是由0开始计数所以需要i-1,当i>=0时进入循环打印当前数组内的数据,i--。 { printf("%c",arr_input[i]); } return 0; }但这样并不完美,我发现在运行后位数只有20位,而输出的有20多位,会导致缓冲区溢出也就是数据写入数组之外的内存区域,而且如果用户输入过大的位数栈会爆掉。
可以加入一个判断从而限制不要超过这一上限,确保栈不会爆掉。也能进行动态分配
if (n <= 0 || n > 1000) { // 设置一个合理上限 printf("位数必须在1-1000之间\n"); return 1; }