目录
数组的概述
1 数组的概念
所谓的数组,指的是相同数据类型数据元素(数据变量)所构成的集合,称为数组; 并且数据集合中的所有元素的存储空间连续。此时数组中的每一个元素可以通过元素序号(数据元素在集合中的连续序号)访问。
2 数组的分类
1. 根据数据元素数据类型分类:
字符数组:char
整型数组:int、short、long
浮点型数组:float、double
结构体数组:struct自定义数据类型等。
2. 根据数组的维度分类:
一维数组、二维数组和多维数组。
一维数组
1 一维数组的定义
所谓的一维数组,指的是数据的维度为一维;
定义语法:
存储类型 数据类型 数组名[常量表达式];
1. 存储类型:修饰的整个数组集合空间的存储属性,也是每一个数组元素存储空间属性。
2. 数据类型:表示每一个数据元素的数据类型,可以是基本数据类型,也可以是构造数据类型;
3. 数组名:表示整个数组集合空间名称,也表示数组首元素的地址;
4. 常量表达式:表示数组集合空间中数据元素的个数,只能使用常量或者常量表达式表示,不能使用变量。
实例:
int a[5]; /* 定义一个有5个int类型数据元素的一维数组 */
2 数组的访问
对于数组的访问不能整体访问,只能逐一元素访问。
数组访问的语法格式:
数组名[下标];/* 下标:实质是数组元素在集合中的元素序号:[0, 元素个数) */
注意:数组元素访问的过程,不要越界,越界会从出现未知异常
-
程序正常执行,数据无错误。
-
程序正常执行,数据异常;
-
程序异常结束,一般会段错误;
3 数组元素的初始值
在定义一维数组的时候,可以定义的时候设置初始值,也可以先只做定义,在设置元素的值。
3.1 先定义,后设置值
在数组定义的时候,会在内存中开辟存储空间,同时数组中的每一个元素有默认初始值。
-
静态数组变量
所谓的静态数组变量,指的是全局数组变量和使用static修饰的局部数组变量,存储在静态存储区
数组中所有元素的默认初始值为0值。
-
自动存储类型数组变量
所谓的自动存储类型数组变量,指的是使用auto修饰的局部变量,存储在栈区
数组中元素的默认初始值为随机值。
3.2 定义同时设置初始值
在定义数组的时候,可以数组集合中所有数据元素全部设置初始值或者部分设置初始值。
3.2.1 全部设置初始值
-
保留常量表达式
int a[5] = {1,2,3,4,5}; /* 将初始值列表中的元素逐一顺序初始化数组元素 */
数组元素的结果值依次:a[0] = 1, a[1] = 2, a[2] = 3, a[3] = 4, a[4] = 5
-
省略常量表达式
数组元素个数等于初始值列表中元素个数。
int a[] = {1,2,3,4,5}; /* 将初始值列表中的元素逐一顺序初始化数组元素 */
数组元素的结果值依次:a[0] = 1, a[1] = 2, a[2] = 3, a[3] = 4, a[4] = 5
3.2.2 部分设置初始值
在部分设置初始值的时候,元素的个数大于初始值列表中元素个数。所以常量表达式不能省略
-
顺序设置初始值
int a[5] = {1,2,3}; /* 将初始值列表中的元素逐一顺序初始化数组元素,未初始化元素的初始值为0值 */
数组元素的结果值依次:a[0] = 1, a[1] = 2, a[2] = 3, a[3] = 0, a[4] = 0
-
指定元素设置初始值
int a[5] = {
[3] = 3, /* 设置数组元素a[3]的初始值,a[3] = 3 */
[2] = 2, /* 设置数组元素a[2]的初始值,a[2] = 2 */
}; /* 在定义的时候,给指定元素设置初始值,未初始化元素的初始值为0值 */
数组元素的结果值依次:a[0] = 0, a[1] = 0, a[2] = 2, a[3] = 3, a[4] = 0
4 一维数组的应用实例
有以下序列:1,1,2,3,5, ……,现倒序输出序列的前20项;
规律:数组的前两项都为1,也就是:a[0] = a[1] = 1;从第二项开始满足:a[i] = a[i-1] + a[i-2];
#include <stdio.h>
#define COUNT 20 /* 定义标识符常量,设置数组元素的个数 */
int main()
{
int i;
int a[COUNT] = {1, 1}; /* 定义数组:部分元素初始化,a[0] = a[1] = 1,此时未初始化元素的初始值为0值 */
/* 设置其它元素:使用规律 a[i] = a[i-1] + a[i-2]; */
for (i = 2; i < sizeof(a)/sizeof(a[0]); i++)
a[i] = a[i-1] + a[i-2];
/* 倒序输出 */
for (i = COUNT-1; i >= 0; i--)
printf("%d ", a[i]);
printf("\n");
}
5 一维字符数组
所谓的字符数组,指的是数据元素的数据类型为char类型的数组,称为字符数组。
5.1 一维字符数组的定义
语法规则:
存储类型 char 数组名[常量表达式];
注意:
1. 存储类型、数组名以及常量表达式满足一维数组的规则;
2. 一维字符数组的数据类型,是char数据类型。
3. 所有的操作都遵循一维数组的规则。
实例:
char buf[5]; /* 在定义一个有5个char数据类型元素的一维字符数组 */
5.2 一维字符数组的访问
对于字符数组的访问和数组访问一致,不能整体访问,只能逐一元素访问。
数组名[下标];下标范围:[0, 数组元素个数),在访问的时候不要越界访问,否则会导致未知异常。
5.3 一维字符数组的初始值
在一维字符数组定义的时候,可以设置初始值,也可以不设置初始值。
5.3.1 先定义,后设置值
在定义一维字符数组的时候,开辟存储空间,字符数据元素会自动设置默认值。
-
静态一维字符数组
全局一维字符数组和static修饰的局部一维字符数组,所有元素的默认初始值为零值(字符'\0'的ASCII编码值,也是数据0值);
-
自动存储类型一维字符数组
auto修饰的局部一维字符数组,元素的默认初始值为随机值。
5.3.2 定义同时设置初始值
在定义一维字符数组同时设置初始值的时候,可以全部设置初始值,也可以部分设置初始值。
-
全部设置初始值
/* 不省略常量表达式的值:需要避免数据元素越界访问 */
char buf[5] = {76,75,74,73,72}; /* 可以使用ASCII编码值给数据元素设置初始值 */
char buf[5] = {'L', 'K', 'I', 'M', 'J'}; /* 直接使用字符给数据元素设置初始值 */
char buf[5] = {"LKJH"}; /* 使用字符串设置数据元素的初始值,注意字符串以'\0'结尾 */
char buf[5] = "LKJH";
/* 省略常量表达式的值,由初始值元素个数确定数组元素个数 */
char buf[] = {76,75,74,73,72}; /* 可以使用ASCII编码值给数据元素设置初始值 */
char buf[] = {'L', 'K', 'I', 'M', 'J'}; /* 直接使用字符给数据元素设置初始值 */
char buf[] = {"LKJH"}; /* 使用字符串设置数据元素的初始值,注意字符串以'\0'结尾 */
char buf[] = "LKJH";
-
部分设置初始值
在部分设置初始值的时候,常量表达式的值不能省略
char buf[5] = {76,75,74};
char buf[5] = {'L', 'K', 'I'};
char buf[5] = {"LK"};
char buf[5] = "LK";
char buf[5] = {
[3] = '76',
[2] = 'a',
};
二维数组
所谓的二维数组,指的是数组的维度为二维的数组,称为二维数组。
1 二维数组的定义
定义的语法规则:
存储类型 数据类型 数组名[行常量表达式][列常量表达式];
注意:
1. 存储类型:修饰整个数组存储空间的属性,同时也是数组元素存储空间属性;
2. 数据类型:表示数组中每一个数据元素的数据类型(具体的某行的某个元素);
3. 数组名:表示整个数组集合变量名,在定义的时候满足标识符的命名规则;
4. 行常量表达式:修饰二维数组中数组元素的行数;
5. 列常量表达式:修饰二维数组中数组元素某一行数据元素的个数(也称为列数);
实例:
int a[3][4]; /* 定义一个有3行4列(int类型数据元素)的二维数组 */
2 二维数组的访问
对于二维数组,不能整体访问,只能(某行中的某个元素)逐一元素访问。
元素访问语法:
数组名行下标;/* 在元素访问的时候不要越界:否则发生未知异常 */
行下标:[0, 数组元素的行数);
列下标:[0, 数组元素的列数);
#include <stdio.h>
int main()
{
int i;
int j;
int a[3][4];
printf("%ld\n", sizeof(a)); //48
printf("%ld\n", sizeof(a[0])); //16
printf("%ld\n", sizeof(a)/sizeof(a[0])); //3
printf("%ld\n", sizeof(a[0][0])); //4
printf("%ld\n", sizeof(a[0])/sizeof(a[0][0])); //4
for (i = 0; i < sizeof(a)/sizeof(a[0]); i++) {
for (j = 0; j < sizeof(a[0])/sizeof(a[0][0]); j++) {
printf("%d ", a[i][j]);
}
printf("\n");
}
}
3 二维数组元素元素初始值
在定义二维数组的时候,可以给数组元素设置初始值,也可以不设置初始值。
3.1 只做定义不做初始化
此时元素的初始值使用默认初始值,由元素的存储位置决定。
-
静态存储数组
全局定义的二维数组和static修饰的局部定义的二维数组,数组存储在静态存储区,此时元素的初始值为零值;
-
auto修饰的数组
局部定义并且未使用static修饰的二维数组,数组存储在栈区,此时元素的初始值为随机值。
3.2 在定义的同时设置初始值
此时元素的初始值为设置的初始值,与存储位置无关。
3.2.1 全部元素初始化
-
保留常量表达式
方式一:
int a[2][3] = {1,2,3,4,5,6}; /* 所有初始化数据逐行逐列元素顺序初始化 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 2, a[0][2] = 3, a[1][0] = 4, a[1][2] = 5, a[1][2] = 6
方式二:
int a[2][3] = {{1,2,3},{4,5,6}}; /* 所有行的初始化数据逐行逐列元素顺序初始化 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 2, a[0][2] = 3, a[1][0] = 4, a[1][2] = 5, a[1][2] = 6
-
省略常量表达式
只能省略行常量表达式,不能省略列常量表达式;
原因:
-
如果省略列常量表达式,此时每一列元素的个数不确定,也就不能得到唯一确定的二维数组;
-
行常量表达式可以省略,在省略的情况下,可以根据每一列元素的个数和初始化元素的个数自动确定二维数组的行数,最终得到唯一确定的二维数组。
方式一:
int a[][3] = {1,2,3,4,5,6}; /* 所有初始化数据逐行逐列元素顺序初始化 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 2, a[0][2] = 3, a[1][0] = 4, a[1][2] = 5, a[1][2] = 6
方式二:
int a[][3] = {{1,2,3},{4,5,6}}; /* 所有行的初始化数据逐行逐列元素顺序初始化 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 2, a[0][2] = 3, a[1][0] = 4, a[1][2] = 5, a[1][2] = 6
3.2.2 部分元素初始化
-
保留常量表达式
方式一:
int a[2][3] = {1,2,3,4}; /* 所有初始化数据逐行逐列元素顺序初始化,未初始化元素的初始值默认设置为0值 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 2, a[0][2] = 3, a[1][0] = 4, a[1][2] = 0, a[1][2] = 0
方式二:
int a[2][3] = {{1},{5,6}}; /* 按行数据实现逐行逐列元素顺序初始化,未初始化元素的默认初始值为0值 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 0, a[0][2] = 0, a[1][0] = 5, a[1][2] = 6, a[1][2] = 0
方式三:
int a[3][4] = {
/* 指定设置第1行 */
[1] = {
[1] = 3, /* a[1][1] = 3 */
[3] = 6, /* a[1][3] = 6 */
},
[0] = {
[0] = 111, /* a[0][0] = 111 */
},
};
-
省略常量表达式
方式一:
int a[][3] = {1,2,3,4}; /* 所有初始化数据逐行逐列元素顺序初始化,未初始化元素的初始值默认设置为0值 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 2, a[0][2] = 3, a[1][0] = 4, a[1][2] = 0, a[1][2] = 0
方式二:
int a[][3] = {{1},{5,6}}; /* 按行数据实现逐行逐列元素顺序初始化,未初始化元素的默认初始值为0值 */
数组元素的初始值值依次:
a[0][0] = 1, a[0][1] = 0, a[0][2] = 0, a[1][0] = 5, a[1][2] = 6, a[1][2] = 0
方式三:
int a[][4] = {
/* 指定设置第1行 */
[1] = {
[1] = 3, /* a[1][1] = 3 */
[3] = 6, /* a[1][3] = 6 */
},
[0] = {
[0] = 111, /* a[0][0] = 111 */
},
};
4 二维数组应用
练习:
使用二维数组实现杨辉三角数据存储并输出;前10行
1 /* 第0行有1个元素 */
1 1 /* 第1行有2个元素 */
1 2 1 /* 第2行有3个元素 */
1 3 3 1
1 4 6 4 1
……
分析:
-
输出杨辉三角的前10行数据,需要定义一个10行10列的二维数组;
-
每行元素中,其实元素和最后一个有效元素,值为1;
第i行:a[i][0] = a[i][i] = 1;
-
从行序号 i = 2开始,每行中间(0 < j < i)的所有元素满足:
a[i][j] = a[i-1][j] + a[i-1][j-1];
#include <stdio.h>
#define N 10 /* 定义表示符常量,标识杨辉三角数据行数 */
int main()
{
int i;
int j;
int a[N][N] = {0}; /* 定义有N行N列的二维数组,并讲数组中所有的元素初始值设置为0 */
for (i = 0; i < sizeof(a)/sizeof(a[0]); i++) {
/* 设置第i行中起始数据元素和最后一个有效数据元素的数据值为1 */
a[i][0] = a[i][i] = 1;
/* 设置第i行中间数据元素的数据值 */
for (j = 1; j < i; j++) {
a[i][j] = a[i-1][j] + a[i-1][j-1];
}
}
/* 数据输出: */
for (i = 0; i < sizeof(a)/sizeof(a[0]); i++) {
for (j = 0; j <=i; j++) {
printf("%d ", a[i][j]);
}
printf("\n");
}
}
注意:
-
在使用二维数组实现杨辉三角数据的存储和输出的时候,存在空间资源的浪费。
-
二维数组的数据元素存储空间,其实质是线性存储空间。其实质可以使用一维数组空间实现二维数据的存储。
使用一维数组实现杨辉三角数据存储和输出思路:
-
线性存储空间资源的开辟,元素的个数与行N的关系:N*(N+1)/2;
-
第i行首元素在一维数组中元素序号和行序号i的关系:i*(i+1)/2;
第i行起始元素和末尾元素的值:
a[i*(i+1)/2] = a[i*(i+1)/2+i] = 1;
第i行中间元素:
a[i*(i+1)/2+j] = a[(i-1)*i/2+j] + a[(i-1)*i/2+j-1];
5 二维字符数组
所谓的二维字符数组,指的是数据元素数据类型为char类型的二维数组,称为二维字符数组。
5.1 定义的语法
存储类型 char 数组名[行常量表达式][列常量表达式];
在数据元素访问的时候满足二维数组元素访问的语法规则。
5.2 数据元素的初始值
5.2.1 只做定义不做初始值设置
数据元素的初始值和存储位置有关
-
静态存储数组:
定义的全局二维字符数数组和定义的static修饰的局部二维数组,如果在定义的时候未设置初始值,那么数组元素的初始值默认为零值('\0'、0);
-
栈区存储二维字符数组:
定义的未被static修饰的局部二维字符数组,如果定义的时候未设置初始值,那么数组元素的初始值默认为随机值。
5.2.2 定义同时设置初始值
数据元素的初始值和设置的初始值有关;
-
全部元素初始化
char str[3][4] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'};
char str[3][4] = {87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98};
char str[][4] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'};
char str[][4] = {87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98};
char str[3][5] = {{"abcd"}, "efgh", "LIJK"};
char str[][5] = {"abcd", "efgh", "LIJK"};
-
部分元素初始化
char str[3][4] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
char str[3][4] = {87, 88, 89, 90, 91, 92, 93, 94, 95, 96};
char str[][4] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j''};
char str[][4] = {87, 88, 89, 90, 91, 92, 93, 94, 95, 96};
char str[3][5] = {{"abcd"}, "ef", "LIJK"};
char str[][5] = {"abcd", "efgh", "LIJK"};
多维数组
所谓的多维数组,指的是数组的维度有多维,常用的三维数组。
1 定义的语法
存储类型 数据类型 数组名[常量表达式][常量表达式]……;
1. 存储类型:表示整个数组集合空间的存储属性,同时也是数组中每一个数据元素的存储类型;
2. 数据类型:表示数组集合中每一个数据元素的存储类型;
3. 数组名:整个数组集合变量名;
4. 常量表达式:对于多维数组,有多个常量表达式构成,每一个常量表达式表示数组的一个维度。
#include <stdio.h>
int main()
{
int i;
int j;
int k;
int a[2][3][4] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24};
for (i = 0; i < sizeof(a)/sizeof(a[0]); i++) {
for (j = 0; j < sizeof(a[0])/sizeof(a[0][0]); j++) {
for (k = 0; k < sizeof(a[0][0])/sizeof(a[0][0][0]); k++) {
printf("%d ", a[i][j][k]);
}
printf("\n");
}
printf("----------\n");
}
}
数组练习
-
约瑟夫环问题:
a) 要求:
现有n个人参加游戏,编号从1开始,游戏的人数使用键盘输入;
输入游戏开始的起始编号num和报数字m,则num从1开始报数,当报数到m则退出环。
输出最后一个退出环的编号。
#include <stdio.h>
#define MAX 100 /* 参与游戏的上限人数为100 */
int main()
{
int count;
int kill_num;
int i;
int num; /* 用于报数 */
int n;
/* 创建一个数组 */
int arr[MAX] = {0};
printf("请输入参与游戏的人数:");
scanf("%d", &count);
printf("请输入游戏参与者的起始编号:");
scanf("%d", &i);
printf("请输入游戏的最大报数值:");
scanf("%d", &kill_num);
num = 1;
n = count;
while(1) {
/* 在环中 */
if (arr[i] == 0) {
if(num == kill_num) {
/* 从环中退出 */
arr[i] = 1;
/* 从下一个在环中的元素从1开始新的报数 */
num = 1;
n --;
if (n == 1)
break;
} else {
num++;
}
}
i = (i+1) % count;
}
for (i = 0; i < count; i++) {
if (arr[i] == 0)
printf("i = %d\n", i);
}
}
2. 编写一个函数实现将一个字符串中的第一个空格和最后一个空格去掉,
比如:"ab cde f gh k",去掉后变成"abcde f ghk";
#include <stdio.h>
int main()
{
int i = 0;
int j;
char buf[] = "ab cde f gh k"; /* 字符串以'\0'结束 */
/* 找的第一个空格序号 */
while(buf[i] != '\0') {
if(buf[i] == ' ')
break;
i++;
}
/* 移动元素,遇到字符'\0'结束 */
while(buf[i] = buf[i+1]) {
if (buf[i] == ' ')
j = i; /* 如果当前字符是空格,记录当前字符序号 */
i++;
}
while(buf[j] = buf[j+1])
j++;
printf("%s\n", buf);
}
3.有一个5*5的二维数组
a) 输出其中最⼤值和最⼩值及其所在的⾏列坐标。
b) 要求删除每行上的最大值,将剩余的数据输出。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int i;
int j;
int arr[5][5]; /* 定义一个5行5列的二维数组 */
int max;
int max_i;
int max_j;
/* 通过随机数给二维数组中的每一个元素设置初始值 */
for (i = 0; i < 5; i++) {
for(j = 0; j < 5; j++) {
srand((i+1)*(j+7)*time(NULL));/* 使用不同的整型数据产生随机因子 */
arr[i][j] = rand()%100;
}
}
for(i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
/* 在二维数组找找到最大元素值及其坐标 */
max = arr[0][0];
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
if (max < arr[i][j]) {
max = arr[i][j];
max_i = i;
max_j = j;
}
}
}
printf("arr[%d][%d] = %d\n", max_i, max_j, max);
for (i = 0; i < 5; i++) {
max = arr[i][0];
max_j = 0;
/* 找到二维数组中每一行元素最大值坐标 */
for (j = 1; j < 5; j++) {
if (max < arr[i][j]) {
max = arr[i][j];
max_j = j;
}
}
/* 删除每行中最大元素 */
for (j = max_j; j < 4; j++) {
arr[i][j] = arr[i][j+1];
}
}
for(i = 0; i < 5; i++) {
for (j = 0; j < 4; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}