C语言学习day09~10

数组的概念

什么是数组

数组是相同类型,有序数据的集合。

数组的特征

数组中的数据被称为数组的元素,是同构的
数组中的元素存放在内存空间里   (char player_name[6]:申请在内存中开辟6块连续的基于char类
型的变量空间)

衍生概念

下标或索引代表了数组中元素距离第一个元素的偏移位置。
数组中元素的地址值,下标越大,地址值越大。(每一块内存空间都有一个独有的内存地址)。
数组的下标是从0开始的。

一维数组

数组的定义

类型说明符 数组名[数组容量]  

说明:
数组的类型说明符由数组中的元素来决定,也就是元素是什么类型,数组就是什么类型

数组名也是标识符, 我们所说的数组(名),大家可以理解为数据类型是数组的变量(名)。

数组容量也可以叫做常量表达式,举例:int ages[10]、int lcd[1024*768]

类型:代表了数组中元素的类型

容量:数组中能存储多少个元素,数组容量可以是一个常量、常量表达式,还可以是符号常量,但必
须是整型。

深入理解:
定义一个数组,相当于申请了一个可以容纳所指定元素数量的内存单元。所申请的内存单元是连续
的。
定义一个数组,相当于定义了多个匿名变量,这些变量可以通过数组名[下标]来标识。

举例

1// 定义一个数组
2int arr[10];
3// 问题:上面数组中,最小下标0,最大下标9

经过上面的案例,分析得到
数组的最大下标 = 数组元素个数(数组容量) - 1

数组元素的访问

原则:数组中的元素不能一次性访问所有元素,只能一个一个的访问。
访问方式:

数组名[下标];

举例

1// 定义一个容纳10个元素的int数组
2int arr[10];
3// 给数组的第一个元素进行赋值
4arr[0] = 89;
5// 访问数组的第一个元素
6int a = arr[0];
7 
8// 问题:以下访问是否正确
9int b = arr[10]; // error,下标越界异常

注意:数组元素的访问一定不能越界。

案例:

1/*
2   需求:一维数组案例-引用数组元素。利用循环给数组元素a[0]~a[9]赋值0~9,并且逆序输出
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 创建一个数组
9    int arr[10];
10 
11    // 使用for循环给数组赋值
12    for(int i = 0;i < 10;i++)
13    {
14        arr[i] = i;        
15    }
16 
17    // 逆序输出
18    // 我们通过循环将数组中的元素一个个取出来,称之为遍历
19    for(int j = 9;j >= 0;j--)
20    {
21        printf("%d ",arr[j]);
22    }
23 
24    printf("\n"); // 纯粹换行
25 
26    return 0;
27}
28 

数组的初始化

定义数组的同时,用指定数据来给对应元素赋值。
简化数组定义后,需要对元素一一赋值操作。
语法规则:

类型 数组名[容量] = {常量1,常量2,常量3...};

注意事项:
数组可以部分初始化:也就是可以给数组中前几个元素初始化,未被初始化的元素系统将自动初始
化,如0;如果定义数组时未指定数据容量,则系统会根据初始化元素的个数来决定数组容量。

1  // 1. 如果定义数组时只给数组前几个初始化,后续剩余元素会自动完成初始化,比如赋值0
2  int arr[10] = {1,2,3,4,5} // 推荐写法,等价于以下写法
3  int arr[10] = {1,2,3,4,5,0,0,0,0,0}
4   
5  // 2. 如果定义数组时未指定数据容量,根据初始化元素个数来决定容量
6  int arr[] = {1,2,3,4,5} // 推荐写法,等价于以下写法
7  int arr[5] = {1,2,3,4,5}

 

衍生概念:
柔性数组:柔性数组的概念是在C99标准,针对结构体的最后一个成员可以是一个未指定大小的数
组;
            广义简单理解:数组容量待定或者待确定的数组,举例:int arr[] = {1,2,3,4,5}

面试题:
在不知道数组类型的情况下,如何确定数组元素的个数
int length = sizeof(arr) / sizeof(arr[0])
说明:

1      1. arr就是我们计算的数组本身,`sizeof(arr)`用来计算该数组中总的字节大小。
2      1. `sizeof(arr[0])`用来计算数组中一个元素所占的字节大小,因为数组中的元素类型相同,所以计算哪一
个都行。
3      1. `sizeof(arr)/sizeof(arr[0])`就是用数组中总的字节数除以每一个元素所占的字节数,从而得到元素
的个数。

一维数组案例

案例1:

1/*
2   需求:一维数组案例-非波拉契数列
3 */
4#include <stdio.h>
5 
6int main()
7{
8    int i;// 循环变量
9    int f[20] = {1,1}; // 定义一个数组,用来存储数列,默认存储第1位和第2位
10    
11    int length = sizeof(f)/sizeof(f[0]);// 计算数组的元素个数  
12 
13    // 生出数列存入数组
14    for(i = 2;i < length;i++)
15    {
16        // 给数组元素赋值,从数组的第3个元素开始
17        f[i] = f[i - 2] + f[i - 1];
18    }
19 
20    // 遍历数组
21    // 通过循环将数组中的每一个元素取出来,称之为遍历
22    for(i = 0;i < length;i++)
23    {
24        // 遍历的时候,要求一行显示5个数
25        if(i % 5 == 0)
26        {
27            printf("\n");
28        }
29        
30        printf("%6d",f[i]);
31    }
32 
33    printf("\n");
34 
35    return 0;
36}
37 

数组的典型应用:数据排序

冒泡排序

向后冒泡
思想:
1. 一次只排好一个数,针对n个数,最差情况需要 n-1次就可以排好
2. 每次排序将相邻数据两两比较,将较大或较小的数据向后交换,等所有数据都比较完成,将较
大/较小的数就会出现在最后,这也是该数应该有的位置。
3. 在余下的数中,再次应用第2步的操作,直到只剩下一个数。
向前冒泡
思想:
1. 一次只排好一个数,针对n个数,最差情况需要n-1次就可以排好
2. 每次排序假定第一个元素是最大/最小的,用第一个元素的后面的元素一一与第一个元素比较,
遇到较大/较小的和第一个元素交换,访问完数组的最后一个元素,就排好了一个数;
3. 在余下的数中,再次应用第2步的操作,直到只剩下一个数。

一维数组案例

案例2

1/*
2  需求:一维数组案例-冒泡排序
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 创建一个数组,用来存储排序的数列
9    int arr[11];
10    int i,j,temp;// i:比较的轮数,j:每一轮比较的次数,temp:临时变量,用来比较时交换数据
11    
12    printf("请输入11个测试整数:\n");
13    
14    // 计算数组的大小
15    int length = sizeof(arr) / sizeof(int); // 这里 sizeof(int) 等价于 sizeof(arr[0]);
16 
17    // 通过循环录入数据
18    for(i = 0;i < length; i++)
19    {
20       scanf("%d",&arr[i]);
21    }
22    
23    printf("\n");
24 
25    // 对数组arr使用冒泡进行排序(注意:我们使用标准冒泡,大家也可以自行补充优化版的冒泡,如鸡尾酒排序法
等等)
26    
27    // 外层循环:控制比较的轮数,因为有11个数,所以我们需要比较 length -1 轮,也就是比较10轮
28    for(i = 0;i < length -1;i++)
29    {
30        // 内层循环:每一轮比较的次数,每一轮比较的次数为 length - 1 - i, 3-1-0=2,3-1-1=1,3-1-2=0
31        for(j = 0;j < length-1-i;j++)
32        {
33            // 相邻的两个数进行比较,满足条件交换位置
34            if(arr[j] > arr[j+1])
35            {
36                temp = arr[j];
37                arr[j] = arr[j+1];
38                arr[j+1] = temp;
39            }
40        }    
41    }
42 
43    printf("冒泡排序后遍历数组:\n");
44 
45    for(i = 0; i < length; i++)
46    {
47        printf("%4d",arr[i]);
48    }
49    
50    printf("\n");
51 
52    return 0;
53}
54 

案例3:

1/*
2   需求:一维数组案例-从键盘输入年、月、日,计算并输出该日是该年的第几天。
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 定义变量:年,月,日,统计总天数,循环变量,用来遍历当前月前面的月份
9    int year,month,day,sum,k;
10    // 定义一个数组,用来存放1~12月每月的天数
11    int t[] = {31,0,31,30,31,30,31,31,30,31,30,31};// 柔性数组
12 
13    printf("请输入年份、月份、天:\n");
14 
15    scanf("%d,%d,%d",&year,&month,&day);
16 
17    // 因为二月比较特殊,存在平年和润年这样的因素,所以需要进行平年和润年的判断
18    if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
19    {
20        t[1] = 29; // 润年 2月 29天
21    }
22    else
23    {
24        t[1] = 28; // 平年 2月 28天
25    }
26 
27    sum = day; // 默认记录最后一个月的天数
28    
29    // 这是该年的第几天
30    for(k = 0; k < month -1;k++)
31    {
32        sum += t[k]; // 叠加前几个月的天数
33    }    
34 
35    printf("%d月%d日是%d年的第%d天。\n",month,day,year,sum);
36 
37 
38    return 0;    
39}
40 

二维数组

定义

二维数组本质上是一个行列式的组合,也就是说二维数组是有行和列两部分构成。二维数组数据是通过行列进行解读。
二维数组可被视为一个特殊的一维数组,相当于二维数组又是一个一维数组,只不过它的元素是一维数组。

语法

类型 数组名[行数][列数];

举例

int arr[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; // 等价于以下写法
int arr[][3] =  {{1,2,3},{4,5,6},{7,8,9}}; // 柔性数组
 
int arr[3][] = {{1,2},{4,5,6},{7}} // 等价于 {{1,2,0},{4,5,6},{7,0,0}}

应用场合

主要应用于数据有行列要求的情况。

特殊写法

下标可以是整型表达式,如:a[2-1][2*2-1] 
下标可以是已经有值的变量或数组元素, 如a[2*x-1][b[3][1]] 
数组元素可以出现在表达式中, 如:b[1][2]=a[2][3]/2

初始化

分行给二维数组赋初值
1int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
可将所有数据写在一个花括号内,按照排列顺序对元素赋值。
1int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; // 等价于上面的写法
可对部分元素赋初值,其余未赋值部分自动填充0 | \0 | 0.0...
1int a[3][4] = {{1},{5,6},{9}}; // 等价于 int a[3][4] = {{1,0,0,0},{5,6,0,0},{9,0,0,0}}
若对全部元素赋初值,自定义数组时可以省略第1为数组的长度,第2维数组的长度必须指明。
1int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
在分行赋初值是,也可以省略第1维的长度。
1int a[][4] = {{1,2,3},{0},{0,10}}; // 等价于 int a[][4] = {{1,2,3,0},{0,0,0,0},{0,10,0,0}}

案例

案例1

1/**
2 * 案例:二维数组的遍历
3 */
4#include <stdio.h>
5 
6int main()
7{
8    int arr[][3] = {{1},{2,4},{5,6,7}};// 经过论证:二维数组我们必须指明第2维的数组的长度
9    // 获取行元素的大小
10    int length = sizeof(arr) / sizeof(arr[0]); // 如果是二维数组,第1维的长度,应该是如左侧这样获取
11    printf("length:%d\n",length);
12 
13    // 遍历二维数组,需要使用双层for循环,外层遍历行,内层遍历每一行的列
14    for(int row = 0;row < length;row++)
15    {
16        // 获取列元素的总大小
17        int len = sizeof(arr[row]) / sizeof(int); 
18        for(int col = 0; col < len; col++)
19        {
20            // 获取列元素
21            printf("%3d",arr[row][col]);// 意思是获取数组arr 第row行第col列
22        }
23        
24        printf("\n");
25    }
26 
27    return 0;
28}
29// 数据类型,分支结构,循环结构,数组

案例2

1/**
2 * 需求:二维数组案例-矩阵的转置(就是将一个2行3列的数组转换为3行2列的数组)
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 准备2个数组,存放转前和转后的数据
9    int arr_before[2][3] = {1,2,3,4,5,6};
10    int arr_after[3][2] = {0};// 初始化,每一列上都是0
11 
12    // 遍历arr_before
13    for(int i = 0;i < sizeof(arr_before)/sizeof(arr_before[0]);i++)
14    {
15        for(int j = 0;j < sizeof(arr_before[i])/sizeof(int);j++)
16        {
17            // 转置
18            arr_after[j][i] = arr_before[i][j];
19        }
20    }
21    
22    // 遍历arr_after
23    
24    for(int i = 0;i < sizeof(arr_after)/sizeof(arr_after[0]);i++)
25    {
26        for(int j = 0;j < sizeof(arr_after[i])/sizeof(int);j++)
27        {
28            // 输出
29            printf("%4d",arr_after[i][j]);
30        }
31        printf("\n");
32    }
33 
34    return 0;
35}
36 

案例3

1/**
2 * 需求:二维数组案例-准备一个int类型的二维数组,求该数组中大的最大值,以及对应的行号,列号
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 准备一个二维数组
9    int arr[3][4] = {{1,2,3,4},{9,8,7,6},{-10,10,-5,2}};
10 
11    // 创建变量,用保存最大值、行号、列号
12    int max = arr[0][0],row=0,col=0;
13 
14    // 遍历集合
15    for(int i = 0;i < sizeof(arr) / sizeof(arr[0]);i++)
16    {
17        for(int j = 0;j < sizeof(arr[i]) / sizeof(arr[i][0]);j++)
18        {
19            // 判断最大值
20            if(arr[i][j] > max)
21            {
22                max = arr[i][j];
23                row = i;
24                col = j;
25            }
26        }
27    }
28    
29    printf("max=%d,row=%d,col=%d\n",max,row,col);
30    return 0;
31 
32}
33 

字符数组

概念

元素类型为char字符型的数组,字符数组往往是用来存储字符串数据的。需要注意的一点是,我们C语言中的字符是字节字符。
字节字符:也就是一个字符占1个字节

语法

char 数组名[容量];
char 数组名[行容量][列容量];

字符数组的语法就是之前所学一维数组和二维数组的语法,只不过数据类型是char而已。
注意:
         如果我们的char数组初始化的时候,没有完全赋完值的时候,空出来的地方使用\0进行填充。

字符案例

案例1

1/**
2 * 需求:字符数组案例-输出一个字符串(I LOVE YOU!)
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 准备一个测试字符串 I LOVE YOU!
9    char arr[11] = {'I',' ','L','O','V','E',' ','Y','O','U','!'};
10 
11    // 通过一个for循环进行遍历输出
12    for(int i = 0;i < sizeof(arr)/sizeof(char);i++)
13    {
14        printf("%c",arr[i]);
15    }
16    printf("\n");
17 
18    return 0;
19}
20 

案例2

1/**
2 * 需求:字符数组案例-输出一个用字符组成的菱形图案
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 准备数据
9    char arr[5][5] = {{' ',' ','*',' ',' '},{' ','*',' ','*',' '},{'*',' ',' ',' ','*'},{' 
','*',' ','*',' '},{' ',' ','*',' ',' '}};
10    
11    // 遍历数组
12    for(int i = 0;i < sizeof(arr)/sizeof(arr[0]);i++)
13    {
14        for(int j = 0;j < sizeof(arr[i])/sizeof(char);j++)
15        {
16            // 打印菱形
17            printf("%c",arr[i][j]);
18        }
19        
20        // 内层循环每结束一轮,就是一行
21        printf("\n");
22    }
23    
24    printf("\n");
25 
26    return 0;
27}
28 

字符串和字符串结束标志

说明

字符数组的多样表示
    
我们的char数组可以以数组的方式一个个输出每个字符;我们的char数组也可以以字符串的方式整体进行输出所有字符。具体如下面案例:

1/*
2   需求:字符数组->字符串
3 */
4#include <stdio.h>

6int main()
7{
8    // 字符串第1种表示:
9    char s1[] = {'h','e','l','l','o',' ','w','o','r','l','d'};
10    // 字符串第2种表示:
11    char s2[] = {"hello world"};
12    // 字符串第3种表示:
13    char s3[] = "hello world";

14 
15    // 测试,使用字符串方式打印,这样就不用一一遍历
16    printf("%s\n",s1);
17    printf("%s\n",s2);
18    printf("%s\n",s3);
19 
20    return 0;
21}
22 

注意
    
字符串的长度与字符数组的长度不一定相同。
利用字符串常量可以对字符数组进行初始化,但不能用字符串常量为字符数组赋值。

字符串的基础操作

字符串输入

        scanf("%s",数组名) 

案例

1/*
2 * 需求:字符数组-字符串的输入输出
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 创建一个变量,用来存放人的名字
9    char name[20];
10 
11    printf("请输入您的姓名:\n");
12    
13    scanf("%s",name);// 如果是字符串,这里不是变量地址,是变量名
14 
15    printf("您的姓名是:%s\n",name);
16 
17    return 0;
18}
19 

注意:采用scanf进行字符串输入,要求字符串中不能存在空格,否则字符串遇到空格会结束。
 

        fgets(数组名,数组容量,stdin);

说明:采用fgets进行字符串输入,可获取所有输入的字符串,包含\n,在实际的字符串处理时,我们
可能需要处理\n

案例

1/*
2 * 需求:字符数组-字符串的输入输出
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 创建一个变量,用来存放人的名字
9    char name[20];
10 
11    printf("请输入您的姓名:\n");
12    
13    fgets(name,sizeof(name)/sizeof(char),stdin);// 输入,返回地址
14    
15    puts(name);// 输出
16 
17    return 0;
18}
19 

注意:
1. 如果输入的字符串不包含空格和换行,可以使用scanf() | fgets();

2. 如果输入的字符串需要包含空格和换行,只能使用fgets();

3. 经过对比,我们发现,在字符串的输入中,fgets()更好;

字符串输出

puts(数组名)

案例

1/*
2 * 需求:字符数组-字符串的输入输出
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 创建一个变量,用来存放人的名字
9    char name[20];
10 
11    printf("请输入您的姓名:\n");
12    
13    scanf("%s",name);
14    
15    puts(name);// 输出 问题?字符串如何拼接
16 
17    printf("您的姓名是:%s\n",name);
18 
19    return 0;
20}
21 

printf("%s",数组名);

案例

1/*
2 * 需求:字符数组-字符串的输入输出
3 */
4#include <stdio.h>
5 
6int main()
7{
8    // 创建一个变量,用来存放人的名字
9    char name[20];
10 
11    printf("请输入您的姓名:\n");
12    
13    scanf("%s",name);
14    
15    printf("您的姓名是:%s\n",name);
16 
17    return 0;
18}
19 
字符串的拼接

strcat(数组名,"需要拼接的字符串") 
 

注意:需要引入#include <string.h>

案例

1/*
2 * 需求:字符数组-字符串的输入输出
3 */
4#include <stdio.h>
5#include <string.h>
6 
7int main()
8{
9    // 创建一个变量,用来存放人的名字
10    char name[20];
11 
12    printf("请输入您的姓名:\n");
13    
14    fgets(name,sizeof(name)/sizeof(char),stdin);// 输入,返回地址
15    
16    puts(strcat(name," 跑着!"));// 拼接
17    return 0;
18}
19 
字符串拷贝

strcpy(数组名,字符串)
注意:需要引入<string.h>库
说明:这个方法适合给字符串赋值用。

字符串比较

strcmp(字符串1,字符串2);
注意:
1. 需要引入<string.h>
2. 返回的结果是boolean

案例

1/*
2 * 需求:字符数组-字符串的输入输出
3 */
4#include <stdio.h>
5#include <string.h>
6 
7int main()
8{
9    // 创建一个变量,用来存放人的名字
10    char username[20];
11 
12    printf("请输入您的账户:\n");
13    
14    scanf("%s",username);
15    
16    if(strcmp(username,"admin"))
17    {
18        printf("账户输入错误!\n");
19    }
20    else
21    {
22        printf("账户输入正确!\n");
23    }
24    
25    return 0;
26}
27 
获取字符窜长度

strlen(字符串)
注意:
1. 需要<string.h>
2. 返回字符串长度

案例

1/*
2 * 需求:字符数组-字符串的输入输出
3 */
4#include <stdio.h>
5#include <string.h>
6 
7int main()
8{
9    // 创建一个变量,用来存放人的名字
10    char username[20];
11 
12    printf("请输入您的账户:\n");
13    
14    scanf("%s",username);
15    
16    // 获取字符串长度
17    unsigned long len = strlen(username);
18    
19    if(strcmp(username,"admin"))
20    {
21        printf("账户输入错误!%lu\n",len);
22    }
23    else
24    {
25        printf("账户输入正确!%lu\n",len);
26    }
27    
28    return 0;
29}
30 

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值