C语言程序设计第10天/数组

数组概念

数组是一组相同类型元素的集合;

  • 数组可以存放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; }