c语言:数组详解一
- 数组
- 数组的概念
- 引例:
- 什么是数组
- 数组的特征:
- 下标(索引)
- 常用的数组
- 按维度划分
- 一维数组
- 数组的定义:
- 数组元素的访问
- 数组的初始化
- **案例一、斐波拉契数列:**
- **案例二、冒泡排序:**
- **案例三、年月日:**
- **二维数组**
- 特殊写法
- 初始化
- 案例一:
- 案例二:
- 案例三:
- 字符数组
- 概念:
- 测试题:
- 语法:
- 注意:
- 字符案例
- 字符串和字符串结束标志
- 字符数组的多样表示
- 字符数组的多样表示
数组
数组的概念
引例:
如果我们在程序中表示一个学生的成绩,我们会用一个int来表示如:int score,如果我们要在程序中表示一组成绩,我们所学的常规的数据类型及无法在表示,这个时候我们就需要用到一种新的表现形式,这种表现形式就是数组
什么是数组
数组是相同类型,有序数据的集合。一定要指定空间的大小
数组的特征:
①:数组中的数据被称为数组的元素,是同构的
②:数组中的元素存放在内存空间中。(如:char ct_name[20]代表我们在内存中申请20块连续的基于char类型的变量空间)
下标(索引)
- 下标或索引代表了数组中元素距离第一个元素的偏移位置。
- 数组中元素的地址值,下标越大,地址值越大。(每一块内存空间都有一个独有的内存地址,变量空间以字节为单位进行地址的标注,我们也可以说 1内存单元 = 1字节)。
- 数组的下标从0开始。
常用的数组
按维度划分
一维数组
数组的定义:
类型说明符 数组名{数组容量}
说明:
-
数组的类型说明符由数组中的元素来决定,也就是元素是什么类型,数组就是什么类型。
-
数组名也是标识符(变量名),我们所说的数组名,可以理解为数据类型是数组的变量名。命名规则与变量一样
-
数组容量也可以叫做元素个数或常量表达式,表达式必须为常量,可以包含常量和符号常量,但不能是变量 符号常量 宏定义#define
int arr[5];#define COUNT 5
int arr[COUNT];
int arr[800 *480];//常量表达式
类型:代表了数组中元素的类型
容量:数组中能存储多少元素,数组容量可以是整数常量表达式,整数符号常量。
- 定义一个数组,相当于申请了一个可以容纳所指定元素数量的内存空间。所申请的内存单元是连续的。
- 定义一个数组,相当于定义了多个匿名的变量,这些变量可以通过
数组名[下标]
来标识
举例
//定义一个数组
int arr[10];//最小索引:0,最大索引:9
//最大索引 = 数组元素个数 -1
数组的最大索引 = 数组的元素个数(数组容量)-1
数组元素的访问
数组中的元素不能一次性访问所有元素,只能一个一个的访问。
语法:
数组名[下标(索引)]
说明:以上语法既能实现数组中元素的取,也能实现数组中元素的存
举例:
//定义一个能够容纳10个元素的int数组
int arr[10]
//给数组的第一个元素进行赋值操作
arr[0] = 89;
arr[0] = 88;
//访问数组的第一个元素
int a = arr[0];//88int b = arr[10];//error:下标越界
数组元素的访问 不能越界
案例:
#include <stdio.h>
//一维数组案例,引用数组元素,利用循环给数组元素赋值10-19,并逆序输出19-10
int arr_test1()
{//创建一个数组int arr[10];//利用for循环给数组元素赋值for(int i = 0;i < 10;i++ ){arr[i] = i + 10;}//逆序输出//通过一个循环将数组中的每一个元素取出,称之为遍历for(int j = 9;j >= 0 ;j--){printf("%-4d",arr[j]);}printf("\n");}
int main()
{arr_test1();return 0;
}
数组的初始化
定义数组的同时,用指定数据来给对应的元素赋初值
简化数组后,需要对元素一一赋初值
语法:
类型 数组名[容量大小] = [常量1,常量2,常量3...];
数组可以部分初始化:也就是可以给数组中前几个元素初始化,未被初始化的元素系统将自动初始化,如果定义数组时,未被指定数组容量,则系统会根据初始化元素的个数来决定数组容量。
//1、如果只给前几个元素初始化,剩余元素会自动完成初始化int arr1[10] = {11,12,13,14,15};//部分初始化
int arr[10] = {11,12,13,14,15,0,0,0,0,0} ;//等价于arr1//2、如果定义数组时未指定数组容量,根据初始化元素的个数来决定容量int arr2[] = {1,2,3,4,5};//数组序容量是5。
int arr3[5] = {1,2,3,4,5};//arr2,arr3等价
衍生概念:
柔性数组 :在c99标准中,针对结构体的最后一个成员可以是一个未指定大小的数组:也就是数组容量待定或待确定的数组
如: int arr[]= {1,2,3,24,3}
- 在不知道数组类型的情况下,如何确定数组元素的个数
int len = sizeof(arr) / sizeof(arr[0]);
说明:
①:arr就是我们计算的数组本身, sizeof(arr)
用来计算该数组中总的字节的大小
②: sizeof(arr[0])
用来计算该数组中第一个元素所占的字节大小,因为数组中元素类型相同,计算第几个都行、
③: sizeof(arr) / sizeof(arr[0]
用数组中总的字节数除以每一个元素所占的字节数,从而得到元素的个数
案例一、斐波拉契数列:
#include <stdio.h>int main()
{int f[20] = {1,1};//定义一个数组用来存储数列 默认存储第一位和第二位//计算获取数组的大小,用于遍历数组int len = sizeof(f) / sizeod(f[0]);//将生成的序列存入数组(绕过0、1)for(int i = 2;i < len ;i++){//给数组元素赋值//从第三个元素开始f[i] = f[i-1] + f[i-2] }//遍历数组for(int j = 0 ; j < len ;j++){//遍历时,一行显示五个数if(j % 5 == 0){printf("\n");}printf("%-8d",f[j]);}printf("\n");}
案例二、冒泡排序:
- 向后冒泡
思想:
-
一次只排好一个数,针对n个数,最差情况需要 n-1次就可以排好
-
每次排序将相邻数据两两比较,将较大或较小的数据向后交换,等所有数据都比较完成,将较大/较小的数就会出现在最后,这也是该数应该有的位置。
-
在余下的数中,再次应用第2步的操作,直到只剩下一个数。
- 向前冒泡
思想:
-
一次只排好一个数,针对n个数,最差情况需要n-1次就可以排好
-
每次排序假定第一个元素是最大/最小的,用第一个元素的后面的元素一一与第一个元素比较,遇到较大/较小的和第一个元素交换,访问完数组的最后一个元素,就排好了一个数;
-
在余下的数中,再次应用第2步的操作,直到只剩下一个数。
#include <stdio.h>
int main()
{int arr[10];//i:外层比较的轮数 j:内存每一轮比较的次数, temp:临时变量用来比较时交换数据int i , j, temp;printf("输入十个测试整数:\n");//计算数组的大小int len = sizeof(arr) /sizeof(arr[0]);//循环录入数据for(i = 0; i < len ;i++){scanf("%d",&arr[i]);}printf("\n");//对数组arr 使用冒泡排序//外层循环:控制比较的轮数,轮数 = 数组容量 - 1for(i = 0;i < len - 1;i++){//内层循环:每一轮比较的次数,for(j = 0;j < len -1 -i; j++){//相邻的两个数比较,满足条件交换位置if(arr[j] > arr[j+1]){temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}printf("冒泡排序后遍历的数组:\n");for (i= 0; i < klen ;i++){printf("%-4d",arr[i]);}printf("\n");
}
案例三、年月日:
#include <stdio.h>
int main()
{//定义变量:年月日,统计总天数,循环变量,用来遍历当前月前面的月份 int year,month,day,sum,k;//定义一个数组用来存放1-12月,每个月的天数int t[] = {31,28,31,30,30,31,30,31,31,30,31,30,31};printf("输入年月日:(用-分隔)\n");scanf("%d-%d-%d",&year,&month,&day);//因为2月特殊存在平闰年的因素 ,要判断处理if((year % 4 == 0 && year % 100 != 0)|| year % 400 ==0)//算数 > 关系> 逻辑{t[1] = 29;//闰年}//先将输入的天记录到总天数sum = day;//计算当前月,前的所有月的天数for(k = 0;k < month -1; k++){sum += t[k];}printf("%d月%d日%d是年的%d天。\n",month,day,year,sum);
}
二维数组
定义:
二维数组本质上是一个行列式的组合,也就是说二维数组是由行和列两部分组成。二维数组数据是通过行列进行解读。
二维数组可被视为一个特殊的一维数组,相当于二维数组是一个特殊的一维数组,只不过它的元素是一维数组。(也就是说数组的元素可以是数组类型)
主要应用于数据有行列要求的情况。
语法:
类型 数组名[行数][列数];
举例:
int arr[3][3] = {{11,12,13},{21,22,23}};// 等价于下arr1
int arr1[][3] = {{11,12,13},{21,22,23}};// 柔性数组int arr[3][3] = {{11,12},{21}};// 等价于arr2(柔性数组,根据最大列中的元素个数分配)
int arr2[3][3] = {{11,12,0},{21,0,0}};int arr3[][] = {{1,2,3},{4,5,6},{7,8,9}};// 这种写法,会编译报错
注意:在C语言中,二维数组在计算机的存储顺序是按行进行的,即第一维的下标变化慢,第二维的下标变化快。
特殊写法
- 下标可以是整型表达式,如:
a[2-1][2*2-1]
- 下标可以是已经有值的变量或数组元素,如 :
a[2*x-1][b[3][1]]
- 数组元素可以出现在表达式中,如:
b[1][2] = a[2][3]/2
注意:使用是数组元素的下标应在已定义数组的大小范围内;应注意区别定义数组大小和引用数组元素的区别。
初始化
- 分行给二维数组赋初值
int arr[2][2] = {{11,22},[33,44]};
- 可将所有数据写在一个花括号内,按照排列顺序对元素赋值
int arr[2][2] = {11,22,33,44};
- 可对部分元素赋初值,其余未赋值部分自动填充
整型默认值:0 | 字符型默认值:\0 | 浮点型默认值:0.0..
int arr[2][2] = {{11,0},{33.0}};
- 若对全部元素赋初值,自定义数组时可以省略第1维数组的长度,但是第2维数组的长度必须指明。
int arr[][2] = {11,22,33,44};
- 在分行赋初值时,也可以省略第1维的长度。
int arr[][2] = {{11,22},[33,44]};
案例一:
#include <stdio.h>
/**
* 需求:二维数组案例-二维数组的遍历
*/
int main()
{// 创建一个二维数组,经过论证:二维数组我们必须指定列的大小,行的大小可以省略int arr[][3] = {{11},{21,22},{31,32,33}};// 获取二维数组的大小,二维数组的大小其实就是行数int len = sizeof(arr)/sizeof(arr[0]);// 遍历二维数组,因为二维数组是行列式,所以要使用到双重for循环int i,j; //定义行和列// 外层循环遍历外层数组,获取行 for(i = 0;i < len;i++){//获取列的元素总大小int len2 = sizeof(arr[i])/sizeof(arr[i][0]);//内层循环遍历内层数组,获取列for(j = 0;j < len2;j++){//打印行列printf("%-5d",arr[i][j]);}printf("\n");}printf("\n");return 0;
}
案例二:
#include <stdio.h>
/*** 需求:二维数组案例-矩阵的转置(就是将一个2行3列的数组转换为3行2列的数组)*/
int arr_test2()
{// 准备2个数组,存放转置前后的数列int arr_before[2][3] = {11, 12, 13, 14, 15, 16}; int arr_after[3][2] = {0}; // 计算arr_before中行数int len1 = sizeof(arr_before) / sizeof(arr_before[0]);// 遍历arr_beforefor (int i = 0; i < r_len; i++){// 计算arr_before中的列数int len2 = sizeof(arr_before[i]) / sizeof(int);for (int j = 0; j < c_len; j++){printf("%-4d", arr_before[i][j]);// 转置 arr_after[j][i] = arr_before[i][j]; }printf("\n");}printf("\n");// 遍历arr_afterint len3 sizeof(arr_after) / sizeof(arr_after[0]);for (int i = 0; i < len3; i++){// 计算arr_after中的列数int len4 = sizeof(arr_after[i]) / sizeof(int);for (int j = 0; j < c_len; j++){printf("%-4d", arr_after[i][j]);}printf("\n");}printf("\n");
}
int main(int argc, char *argv[])
{arr_test2();return 0;
}
转置:arr_after[j] [i]= arr_before[i] [j];
案例三:
#include <stdio.h>
/**
* 需求:二维数组案例-准备一个int类型二维数组,求该数组中最大的值,以及对应的行号和列号。
*/
int arr_test3()
{// 准备一个二维数组int arr[][4] = {{1,2,3,4},{9,8,7,6},{-10,10,-5,2}};// 创建变量:最大值、行号、列号int max = arr[0][0],row = 0,col = 0;// 遍历集合for(int i = 0; i < sizeof(arr)/sizeof(arr[0]);i++){for(int j = 0;j < sizeof(arr[i])/sizeof(arr[i][0]);j++){// 判断最大值if(arr[i][j] > max){max = arr[i][j];row = i;col = j;}}}printf("max=%d,row=%d,col=%d\n",max,row,col);
}
int main(int argc,char *argv[])
{arr_test3();return 0;
}
字符数组
概念:
元素类型为char字符型的数组,字符数组往往是用来存储字符串数据的。注意:我们c语言中的字符是字节字符
字节字符:也就是一个字符占一个字节,在c语言中我们用char来表示字节。
测试题:
char a = 'A'; // 正确
char b = '1'; // 正确
char c = 65; // 正确,ASCII码char d = "A"; // 错误,这是字符串的写法,也是char数组的写法
char e = '司'; // 错误,中文一个字符超过1个字节
语法:
char 数组名[容量];
char 数组名[行容量][列容量];
字符数组的语法就是我们前面所学的一维数组和二维数组的语法,只不过数据类型是char而已。
注意:
如果我们的char数组初始化的时候,没有完全赋完值的时候,空出来的地方使用\0进行填充
如:char a[8] = {'h','e','l','l','o'};
等价于 char a[8] = {'h','e','l','\0','\0'}
字符案例
案例一:
#include <stdio.h>
/**
* 需求:字符数组案例-输出一个字符串(I LOVE YOU!)
*/
int char_test1()
{// 准备一个测试数组 ASCII中码0和码32对应的不是同一类的空char arr[11] = {'I',' ','L','O','V','E',32,'Y','O','U','!'};// 通过一个for循环进行遍历输出int len = sizeof(arr)/sizeof(char);for(int i = 0; i < len;i++){printf("%c",arr[i]);}printf("\n");
}
int main(int argc,char *argv[])
{char_test1();return 0;
}
案例二:
#include <stdio.h>
/**
* 需求:字符数组案例-输出一个用字符组成的菱形图案
*/int main(int argc,char *argv[])
{// 准备数据char arr[5][5] = {{' ',' ','*',' ',' '},{' ','*','*','*',' '},{'*','*','*','*','*'},{' ','*','*','*',' '},{' ',' ','*',' ',' '}};// 遍历数组int len1 = sizeof(arr)/sizeof(arr[0]);for(int i = 0; i < len1;i++){int len2 = sizeof(arr[i])/sizeof(char);for(int j = 0; j < len2;j++){printf("%c",arr[i][j]);}// 每一行中的列元素输出完毕,执行换行printf("\n");}printf("\n");return 0;
}
注意:
①:如果定义时不初始化,元素值不确定:
char arr1[2]; //未初始化,元素值是不确定的(随机的) char arr2[5] = {'a','b','c'}; //初始化不完全,未初始化的部分用'\0'来填充
②:如果提供的字符个数大于数组长度(越界),则按语法错误处理;如果字符个数小于数组长度,后面的元素自动为空字符(\0)。
③:如果提供的字符个数与数组长度相同时,可省略数组长度,系统会自动确定元素个数,适合字符个数较多时。
字符串和字符串结束标志
说明:
- C语言规定,字符串以字符 \0 作为结束标志。
- 编译系统对字符串常量自动加一个 \0 作为结束标志。
- 程序中往往通过判断 \0 来检测字符串是否结束。
- \0 的ASCII码为0,不是一个可显示的字符,是“空操作符”,它什么都不做,不会增加有效字符,仅是一个工判别的标志。
字符数组的多样表示
在x语言中我们的char数组可以以数组的方式一个个输出每个字符;我们的char数组也可以以字符串的方式整体进行输出所有字符。
例:
#include <stdio.h>
int main()
{// 字符串的第1种表示:char s1[] = {'h','e','l','l','o',' ','w','o','r','l','d'};// 字符串的第2种表示:char s2[] = {"hello world"};// 字符串的第3种表示:char s3[] = "hello world";// 输出字符串,printf("%s,%s,%s\n",s1,s2,s3);// hello world,hello world,hello world
}
注意:
- 字符串的长度与字符数组的长度不一定相同
- 利用字符串常量可以对字符数组进行初始化,但不能用字符串常量为字符数组赋值
// 正确演示,利用字符串常量可以对字符数组进行初始化 char arr1[6] = "hello";// 错误演示,用字符串常量为字符数组赋值。 char arr2[6]; arr2 = "hello";
符,仅是一个工判别的标志。
字符数组的多样表示
在x语言中我们的char数组可以以数组的方式一个个输出每个字符;我们的char数组也可以以字符串的方式整体进行输出所有字符。
例:
#include <stdio.h>
int main()
{// 字符串的第1种表示:char s1[] = {'h','e','l','l','o',' ','w','o','r','l','d'};// 字符串的第2种表示:char s2[] = {"hello world"};// 字符串的第3种表示:char s3[] = "hello world";// 输出字符串,printf("%s,%s,%s\n",s1,s2,s3);// hello world,hello world,hello world
}
注意:
- 字符串的长度与字符数组的长度不一定相同
- 利用字符串常量可以对字符数组进行初始化,但不能用字符串常量为字符数组赋值
// 正确演示,利用字符串常量可以对字符数组进行初始化 char arr1[6] = "hello";// 错误演示,用字符串常量为字符数组赋值。 char arr2[6]; arr2 = "hello";