第七章 初窥天机之数组处理

为什么要引用数组?我们此处引用一个经典的例子,那就是学生成绩记录问题。假如一个班有40名学生,每个学生都有一个学号,要把所有学生的学号保存起来,怎么办?根据之前章节的讲解我们可能会为每一个学生分配一个变量,用来保存学号。这样,就会有40个变量需要定义。可是,如果是全校成千上万个学生的学号呢?难道我们还是这样为每个学生分配一个变量吗?那么且不说后续的学号怎么使用,单单定义这些学号变量,是不是就会浪费极大地时间呢?这根本不利于程序的快速开发和管理。如果有一个学号出错,导致后续的学号都得更改的话岂不是就得“哭爹喊娘”。所以我们引入了数据的概念。

数组就是用同一个名字命名把相同数据类型的元素按一定顺序排列的集合。然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标。如下:

int a=1;b=2;c=3

改写成:

int a[3] = {1,2,3};

‘a’为数组名,数据类型为“int”型。这样省去很多变量书写,提高编程效率。

当然数组可不只有“int”型,还有“char”型,“float”型,“double”型等数据类型。当然数组也不是仅仅一维,还可以是二维、三维,甚至是多维。想知道这些知识的详细信息吗?精彩就在下面,,,

7.1 数组的概念

7.1.1 数组的概念

数组就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用同一个名字命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。数组是在程序设计中,为了处理方便,把具有相同类型的若干变量按有序的形式组织起来的一种形式。这些按序排列的同类数据元素的集合称为数组。

比如:

int a[5];

说明整型数组a,有10个元素。如图7.1所示为一维数组的逻辑结构图。

图7.1 一维数组逻辑结构图

 

7.1.2 数组的分类

按照不同的分类标准,可以对数组进行不同的划分。

按照数组中元素的数据类型,将数组划分为整型数组、实型数组、字符数组、指针数组、结构体数组、联合体数组等。

比如:

int a[5];//说明整型数组a,有10个元素。

float b[10];//说明实型数组b,有10个元素。

double c[15];//说明实型数组c,有15个元素。

char d[20];//说明字符型数组d,有20个元。

struct array{

    int a;

    float b;

    double c;

    char d;

}Array[5];//说明结构体数组Array,有5个元素,每个元素有4个不同类型的变量组成。

 

按照数组下表的维数不同,将数组分为一维数组、二维数组、三维数组、多维数组等。

比如:

int a[5];//一维整型数组,数组存储空间大小为5。

char b[5][5];//二维字符数组,数组存储空间大小为5×5。

double c[5][5][5];//三维数组,数组存储空间大小为5×5×5。

 

在接下来的章节中主要讲解一维数组、二维数组、三维数组、字符数组的使用,指针数组、结构体数组、联合体数组将在后续章节中介绍。

 

7.2 一维数组

7.2.1 一维数组的定义

一维数组是由数字组成的以单纯的排序结构排列的结构单一的数组。是二维数组和三维数组的基础。

一维数组的一般形式:

类型符 数组名[常量表达式];

说明:

  1. 数组名的命名规则和变量名相同,遵循标识命名规则。
  2. 在定义数组时,需要制定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组长度。
  3. 常量表达式中可以包含常量和和符号变量。

比如图7.1所示,为int a[5]的逻辑结构图。

 

7.2.2 一位数组的初始化

由于一维数组可以分为整型数组、浮点型数组、字符型数组,结构体数组等。所以初始化也分为几种方式。但是总体差异不大,除了字符型数组的操作外。

1. 一维整型数组初始化:

方式1:

int a[5] = {1,2,3,4,5};

方式2:

int a[5];

a[0] = 1;

a[1] = 2;

a[2] = 3;

a[3] = 4;

a[4] = 5;

定义一个整型数组,整型数组的存储空间为5。由于数组的存储空间下标是从0开始的,所以第一个存储空间为a[0],第二个存储空间为a[1],…,第五个存储空间为a[4]。它们分别存储为1,2,…,5。当然还可以用一个循环实现存储。如下:

方式3:

int a[5];

for(int i = 0 ; i<5 ; i++)

{

    a[i] = i+1;

}

循环把i+1的值赋值给a[i],实现与上面一样的效果。

2. 一维浮点型数组初始化和一维字符型型数组初始化与一维整型数组的初始化本质方式一样。一维浮点型数组初始化时需要定义成浮点型,比如:double b[5] = {1.1,2.2,3.3,4.4,5.5};。一维字符型型数组初始化时需要定义成字符型,比如:char c[5]={‘A’,’B’,’C’,’D’,’E’};。当时还有就是如果我们使用字符串赋值时,就需要使用相关的函数了,我们将会在下一章进行详细讲解。

3. 一维结构体数组初始化:

方式1:

struct array

{

    int a;

    double b;

    char c;

}ARRAY[2]={1,1.1,’a’,2,2.2,’b’};

或者是ARRAY[2]={{1,1.1,'a'},{2,2.2,'b'}};。把每一个结构体长度对应的变量赋值用{}括起来。

方式2:

struct array

{

    int a;

    double b;

    char c;

}ARRAY[2];

ARRAY[0].a = 1;

ARRAY[0].b = 1.1;

ARRAY[0].c = 'a';

ARRAY[1].a = 2;

ARRAY[1].b = 2.2;

ARRAY[1].c = 'b';

方式3:

for(i = 0 ; i < 2 ; i++ )

{

    scanf("%d-%lf-%c", &ARRAY[i].a,&ARRAY[i].b, &ARRAY[i].c);

}

方式3是通过输入的值赋值给变量。整体看起来方式1和方式3更简洁。所以在程序设计中方式1和方式3使用的更多。方式1通常用于初始化,方式3多用于改变结构体变量的值,通过运行程序获得想要结果。

除了以上的多种初始化方式以外,还有其他的初始化方式.

比如:

int a[]={1,2,3,4,5};

这个初始化就会默认为分配各a的数组存储空间长度为5,几个元素就是默认分配几个长度内存空间。这种方式也会时常使用。

注:

  1. 定义的任何未初始化的数组,其内容未知。
  2. 定义的任何已初始化的数组,但初始化数据小于定义的数组长度时,剩余未初始化的数据默认为0。

7.2.3 一位数组的使用

在C语言中数组元素只能逐个引用,不能够一次引用整个数组。

一维数组引用的一般形式:

数组名[下标值];

说明:

  1. 如果定义的数组长度为N,则下标值的取值范围为0~N-1。
  2. 如果取得下标值大于N-1,称之为下标越界。有些编译器不会检查下标越界问题。 就是说如果下标越界不会报错,而会产生未知的数据,影响程序结果。比如a[N+2]就是指向未知的内存空间。

 

其实,在上一节一维数组的初始化中我们已经使用了数组的引用。比如初始化的方式2和方式3。再比如:

int a[5]={1,2,3,4,5};

for(i=0; i<5 ; i++)

{

    printf(“%d\n”, a[i]);

}

其中,printf中的a[i]就表示每个i小标对应一个元素值。

 

例7.1】一个一维数组正序输入,逆序输出。

解题思路:首先定义一个一维数组,通过循环0~N-1依次初始化数据或者通过键盘输入一组数据,然后依次输出为N-1~0的下标。

编写程序:

#include <stdio.h>
#define N 10//宏定义
int main()
{
	int i,a[N];//数组a的大小可以通过宏定义来确定长度
	for(i = 0 ; i <= N-1 ; i++ )//依次初始化数据0~N-1
	{
		a[i] = i+1;
	}
	for(i = N-1 ; i >= 0 ; i-- )//依次输出下标为N-1~0的数据
	{
		printf("%d\n", a[i]);
	}
	return 0;
}

运行结果:

10

9

8

7

6

5

4

3

2

1

Press any key to continue

 

程序分析:程序第6~9行为数据从1~10的初始化。数组下标是从0~9变化的。程序第10~13行,就是逆序输出的核心代码,为了逆序输出结果,最简单的方式就是直接让下标从大到小变化。所以我们从9~0变化。N-1就是数值9。定义成N提高程序的通用性。比如要进行20的逆序输出,就只需要更改宏定义中的10为20即可。

此处添加一维数组有关的代码。

【】

 

7.3 二维数组

7.3.1 二维数组的定义

二维数组常称为矩阵,把二维数组改写成行和列的排列形式,可以有助于形象化地理解二维数组的逻辑结构,在实际编程中使用频率最高。

二维数组的一般形式:

类型符 数组名[常量表达式][常量表达式];

或者理解为:

类型符 数组名[行常量表达式][列常量表达式];

二维数组是特殊的一维数组,它的一个行元素可以看作为一个一维数组。

比如:

float a[5][5];

定义了一个5行5列的整型数组。比如,a[0]就是可以认为是一个含有5个元素的一维数组,即a[0][5]。其他的元素类似。如图7.2所示,为二维数组的逻辑结构图。

图7.2 二维数组逻辑结构图

 

C语言中二维数组的存储结构是按照“行优先”的顺序进行存储的。比如:float f[2][3]的实际内存存储结构如图7.3所示。

图7.3 二维数组的存储结构

注:并不是所有的编程语言都是按“行优先”进行存储的,比如C,C++等。有些是按照“列优先”进行存储的,比如VB等。

7.3.2 二位数组的初始化

同样二维数组可以分为整型二维数组、浮点型二维数组、字符型二维数组,结构体二维数组等。所以初始化也分为几种方式。但是总体差异不大,此处我们主要讲解整型二维数组初始化和浮点型二维数组初始化。

1. 二维整型数组初始化:

方式1:

int a[2][3]= {1,2,3,4,5,6};或者int a[2][3]= {{1,2,3},{4,5,6}};

方式2:

int a[2][3];

a[0][0] = 1;

a[0][1] = 2;

a[0][2] = 3;

a[1][0] = 4;

a[1][1] = 5;

a[1][2] = 6;

定义一个2×3整型二维数组,整型二维数组的存储空间为6。由于数组是按照“行优先”进行存储的,所以第一个存储空间为a[0][0],第二个存储空间为a[0][1],…,第六个存储空间为a[1][2]。它们分别存储为1,2,…,6。当然还可以用两重循环实现存储。如下:

方式3:

int a[2][3];

for(i = 0 ; i < 2 ; i++ )

{

    for(j = 0 ; j < 3 ; j++ )

    {

        a[i][j] = (i*3+j)+1;

    }

}

该循环实现与上面方式1、方式2一样的效果。外层循环控制一维数组的个数。内层循环控制对应一维数组中具体的数值,比如第一个数组中第二个元素是a[0][1]。a[i][j] = (i*3+j)+1实现了把1赋值给a[0][0],2赋值a[0][1],...,6赋值给a[1][2]的功能。

2. 二维浮点型数组初始化与二维整型数组的初始化本质方式一样。二维浮点型数组初始化时需要定义成浮点型,比如:double b[2][2] = {{1.1,2.2},{3.3,4.4}};。其他方式完全一样。 3. 二维结构体数组初始化:

方式1:

struct array

{

    int a;

    double b;

    char c;

}ARRAY[2][2]={1,1.1,’a’,2,2.2,’b’,3,3.3,’c’,4,4.4,’d’};

或者:

ARRAY[2][2]={{{1,1.1,’a’},{2,2.2,’b’}},{{3,3.3,’c’},{4,4.4,’d’}}};

就是把每一个结构体长度对应的变量赋值用{}括起来。

方式2:

struct array

{

    int a;

    double b;

    char c;

}ARRAY[2][2];

ARRAY[0][0].a = 1;

ARRAY[0][0].b = 1.1;

ARRAY[0][0].c = 'a';

ARRAY[0][1].a = 2;

ARRAY[0][1].b = 2.2;

ARRAY[0][1].c = 'b';

ARRAY[1][0].a = 3;

ARRAY[1][0].b = 3.3;

ARRAY[1][0].c = 'c';

ARRAY[1][1].a = 4;

ARRAY[1][1].b = 4.4;

ARRAY[1][1].c = 'd';

方式3:

for(i = 0 ; i < 2 ; i++ )

{

    for(j = 0 ; j < 2 ; j++ )

    {

        scanf("%d-%lf-%c", &ARRAY[i][j].a,&ARRAY[i][j].b, &ARRAY[i][j].c);

    }

}

方式3是通过输入的值赋值给变量。整体看起来方式1和方式3更简洁。所以在程序设计中方式1和方式3使用的更多。方式1通常用于初始化,方式3多用于改变结构体变量的值,通过运行程序获得想要结果。

 

除了以上的多种初始化方式以外,还有其他的初始化方式.

比如:

int a[][3]={1,2,3,4,5,6};

这样初始化程序会自动进行计算,通过的初始化元素的个数来算出二维数组中第一个下标值。因为该初始化数组有6个元素,且第二个下标为3,所以就可以求出第一个下标为6/3=2,即第一个下标就是2。完整的写出该数组为:int a[2][3]={1,2,3,4,5,6}。

说明:

  1. 初始化二维数组,第一个下标可以省略,但是第二个下标不能省略。
  2. 如果初始化的二维数组中元素的个数不能够被第二个下标的整除,则依次把元素的个数加1,除以第二个下标,直到能够整除为止,即此时的值就是第一个下标的值。

比如:

int a[][3] ={1,2,3,4,5,6,7};

该数组中一个有7个元素,我们用7除以3,不能整除,所以7加1等于8。然后用8除以3,还是不能整除。再让8加1等于9。此时用9除以3正好能够整除,所以该初始化数组的第一个下标是3。

  1. 针对(2)中,发现真正的分配的内存空间为9,而实际初始化了7个元素,那么其他未被初始化的第8,9个元素,会默认初始化为0。但如果数组只定义,未赋初值,比如:int a[2][2]。则数组不会默认赋值为0,而是指向未知的区域。
  2. 不能未赋值而初始化成如下形式:

int a[][2];

这样是不合法的,因为编译器并不知道要分配多少内存空间。

7.3.3 二位数组的使用

在C语言中二维数组同一维数组一样,元素只能逐个引用,不能够一次引用整个数组。

二维数组引用的一般形式:

数组名[行下标值][列下标值];

说明:

  1. 如果定义的数组长度为N*M,则下标值的取值范围为(0~N-1)*(0~M-1)。
  2. 如果取得行下标值大于N-1,称之为行下标越界。

比如:

double a[2][3];

a[2][1] = 5.6;

由于定义了2*3的二维数组,行下标取值范围0~1,列下标取值范围0~2。a[2][1]行下标超过数组的范围。同样,如果列下标值大于M-1,称之为列下标越界。比如:a[1][3] = 6。有些编译器不会检查下标越界问题。就是说如果下标越界不会报错,而会产生未知的数据,影响程序结果。比如a[2][3]就是指向未知的内存空间。

 

例7.2】从键盘输入一个2*3的二维数组,然后输出。

解题思路:因为是二维数组,我们定义两重循环。外层循环控制行(2行),内层循环控制列(3列)。

编写程序:

#include <stdio.h>
int main()
{
	int i,j,arr[2][3];//arr[2][3]为二维数组的定义
	for(i = 0 ; i < 2 ; i++ )//外层循环控制行
	{
		for(j = 0 ; j < 3 ; j++ )//内层循环控制列
		{
			scanf("%d", &arr[i][j]);//输入数据
		}
	}
	for(i = 0 ; i < 2 ; i++ )//输出结果
	{
		for(j = 0 ; j < 3 ; j++ )
		{
			printf("%d\n", arr[i][j]);//输出输入的数据
		}
	}
	return 0;
}

运行结果:

1 2 3 4 5 6

1

2

3

4

5

6

Press any key to continue

程序分析:程序5~11行为二维数组数据的输入,第7~10行为输入的内层循环。程序第12~18行为程序输出结果。

 

例7.3】从键盘输入一个2*3的数组,然后转置输出。

解题思路:因为是二维数组,我们定义两重循环。外层循环控制行(2行),内层循环控制列(3列)。

我们需要定义两个二维数组,第一个二维数组是2行3列,作为初始化数组。第二个二维数组是3行2列,作为目的数组。只需要把初始化数组的列转换成目的数组的行,把初始化数组的行转换成目的数组的列。

编写程序:

#include <stdio.h>
#define M 2	//宏定义
#define N 3
int main()
{
	//source[M][N]是初始化数组,dest[N][M]是目的数组
	int source[M][N]={{1,2,3},{4,5,6}},dest[N][M];
	int i,j;
	printf("初试数组:\n");
	for ( i = 0 ; i < M ; i++)//外层循环,控制行
	{
		for ( j = 0 ; j < N ; j++)//内层循环,控制列
		{
			printf("%d ", source[i][j]);//输出初始化数组
		}
		printf("\n");//输出一行后输出换行符,换一行。
	}
	//该循环实现矩阵的转置
	for ( i = 0 ; i < M ; i++)
	{
		for ( j = 0 ; j < N ; j++)
		{
			dest[j][i] = source[i][j];//把行换成列,把列换成行
		}
	}
	printf("目的数组:\n");
	for ( i = 0 ; i < N ; i++)
	{
		for ( j = 0 ; j < M ; j++)
		{
			printf("%d ", dest[i][j]);
		}
		printf("\n");
	}
	return 0;
}

运行结果:

初试数组:

1 2 3

4 5 6

目的数组:

1 4

2 5

3 6

Press any key to continue

程序分析:程序第7行为初始化源二维数组。程序第10~17行输出初始化结果。程序18~25行为实现程序转置的核心代码,就是把两行三列转换成三行两列。程序27~34行为输出转置后的结果。

 

7.4 三维数组

7.4.1 三维维数组的定义

如果二维数组称为平面上的数组,那么三维数组就会被称为立体上的数组。把三维数组改写成行、列和高的排列形式,可以有助于形象化地理解三维数组的逻辑结构。

三维数组的一般形式:

类型符 数组名[常量表达式][常量表达式][常量表达式];

或者理解为:

类型符 数组名[高常量表达式][行常量表达式][列常量表达式];

三维数组是特殊的二维数组,它的一个高度元素可以看作为一个二维数组。或者理解为多个二维数组的集合。

比如:

int a[5][5][5];

定义了一个高度为5,每一维高度对应一个5行5列的整型二维数组。比如,a[0]就是可以认为是一个5行5列的二维数组,即a[0][5][5]。其他的元素类似。或者说我们定义了5个形式为a[5][5]的二维数组。第几层高就是第几个二维数组。如图7.4所示,为三维数组的逻辑结构图。

图7.4 三维数组逻辑结构图

C语言中三维数组的存储结构是按照“行优先”的顺序进行存储的。比如:float f[2][2][2]的实际内存存储结构如图7.5所示。

图7.5 三维数组的存储结构

 

7.4.2 三位数组的初始化

三维数组可以分为整型三维数组、浮点型三维数组、字符型三维数组,结构体三维数组等。所以初始化也分为几种方式。但是总体差异不大,此处我们主要讲解整型三维数组初始化和浮点型三维数组初始化。

1. 三维整型数组初始化:

方式1:

int a[2][2][2]= {1,2,3,4,5,6,7,8};

或者

 

    

小括号里面的表示2列,每两个元素作为集合括起来的是2行,用括号把两个行括起来的的是2高。对应等号右边的三维数组,从右向左分别是列、行、高。

方式2:

int a[2][2][2];

a[0][0][0] = 1;

a[0][0][1] = 2;

a[0][1][0] = 3;

a[0][1][1] = 4;

a[1][0][0] = 5;

a[1][0][1] = 6;

a[1][1][0] = 7;

a[1][1][1] = 8;

定义一个2×2×2整型三维数组,整型三维数组的存储空间为8。具体存储结构参考图5.5三维数组的存储结构。当然还可以用两重循环实现存储。如下:

方式3:

int a[2][2][2];

for ( k = 0 ; k < 2 ; k++ )//控制高

{

    for(i = 0 ; i < 2 ; i++ )//控制行

    {

        for(j = 0 ; j < 2 ; j++ )//控制列

        {

            scanf("%d", &a[k][i][j]);//各个元素赋值

        }

    }

}

该循环实现与上面方式1、方式2一样的效果。最外层循环控制三维数组中的高,中层循环控制三维数组中的高,内层循环控制三维数组中的列。

2. 三维浮点型数组初始化与三维整型数组的初始化本质方式一样。

三维浮点型数组初始化时需要定义成浮点型。比如:double b[2][2] = {{1.1,2.2},{3.3,4.4}};。其他方式完全一样。

除了以上的多种初始化方式以外,还有其他的初始化方式比如:

int a[][2][2]= {1,2,3,4,5,6,7,8};

这样初始化程序会自动进行计算,通过的初始化元素的个数来算出三维数组中第一个下标值。计算方式同二维数组方式类似。完整的写出该数组为:int a[][2][2]= {1,2,3,4,5,6,7,8};

说明:

(1) 初始化三维数组,第一个下标可以省略,其他下标不能省略。

(2) 如果第一个下标省略,求解第一个下标的方法如下:

a. 初始化三维数组中的元素。

b. 判断元素的个数是否能够被第三个(从左到右数第三个)下标的整除。如果能够整除,转c,否则转f。

c. 用b中的结果值,除以第二下标,如果能够整除,转d,否则转f。

e. 从c中得到的结果就是第一个下标的值。

f. 元素的个数自动加1,转b。

g. 执行下面的语句。

 

比如:

int a[][2] [2]={1,2,3,4,5,6,7};

该数组中一个有7个元素,我们用7除以2,不能整除,所以7加1等于8。然后用8除以2,能够整除。让8除以2的结果4除以2,能够整除。所以该初始化数组的第一个下标是2。

(3) 针对(2)中,发现真正的分配的内存空间为8,而实际初始化了7个元素,那么其他未被初始化的第8个元素,会默认初始化为0。但如果数组只定义,未赋初值,比如:int a[2][2][2]。则数组不会默认赋值为0,而是指向未知的区域。

(4) 不能未赋值而定义成如下形式:

int a[][2][2];

这样的定义是不合法的。

 

7.4.3 三位数组的使用

三维数组同二维数组和一维数组一样,元素只能逐个引用,不能够一次引用整个数组。

三维数组引用的一般形式:

数组名[高常量表达式][行常量表达式][列常量表达式];

说明:

  1.  如果定义的数组长度为G*N*M,则下标值的取值范围为(0~G-1)*(0~N-1)*(0~M-1)。
  2.  如果取得高下标值大于G-1,称之为行下标越界。

比如:

int a[2][3][4];

a[2][1][2] = 5;

由于定义了2*3*4的三维数组,高下标取值范围0~1,行下标取值范围0~2,列下标取值范围0~3。a[2][1][2]高下标超过数组的范围。如果行下标值大于N-1,称之为行下标越界。比如:a[1][4][2] = 4。如果列下标值大于M-1,称之为列下标越界。比如:a[1][2][4] = 6。有些编译器不会检查下标越界问题。就是说如果下标越界不会报错,而会产生未知的数据,影响程序结果。比如a[2][3][4]就是指向未知的内存空间。

 

例7.4】输入一个三维数组的数据,按输入的顺序输出结果。

解题思路:我们用三个变量分别控制高、行和列。然后再定义一个变量依次初始化三维数组。最后格式输出三维数组的结果。

编写程序:

#include <stdio.h>
int main()
{
	int i,j,k;//定义变量
	int num[2][3][4];//定义三维数组
	int count = 1;//作为初始化三维数组的值
	for ( i = 0 ; i < 2 ; i++ )//控制高
	{
		for ( j = 0 ; j < 3 ; j++ )//控制行
		{
			for ( k = 0 ; k < 4 ; k++ )//控制列
			{
				//初始化三维数组,count值先赋值给三维数组num,然后在自加1
				num[i][j][k] = count++;
			}
		}
	}
	//输出两个大小为3*4的二维数组。
	for ( i = 0 ; i < 2 ; i++ )
	{
		for ( j = 0 ; j < 3 ; j++ )
		{
			for ( k = 0 ; k < 4 ; k++ )
			{
				printf("%2d ", num[i][j][k]);//输出格式参考第二章
			}
			printf("\n");//每行添加换行符,进行换行
		}
		//二维数组之间空一行,同时最后一个二维数组不需要输出数组间隔
		printf("%s", i<1?"\n":"");
	}
	return 0;
}

运行结果:

 1  2  3  4

 5  6  7  8

 9 10 11 12

 

13 14 15 16

17 18 19 20

21 22 23 24

Press any key to continue

程序分析:在程序7~17行初始化三维数组,最外层循环控制的是三维数组的高,中间层循环控制三维数组的行,最内层循环控制三维数组的列。第14行为赋值语句。程序第19~31行为输出三维数组的结果。第27行表示每输出一行结果后换行。程序第30行为两个三维数组之间的格式控制,如果不是最后一个三维数组那么就输出换行。如果是最后一个三维数组,那么就不输出表示三维数组分界的换行。

7.5 字符数组

7.5.1 字符数组的定义

在第二章我们已经介绍了关于字符和字符串的输入输出函数。现在我们具体讲解字符和字符串的具体存储结构。

字符数组是存放字符量的数组。字符数组中的一个元素存放一个字符,它在内存中占用两个字节。一个汉字占用两个字节。字符数组类型说明的形式与前面介绍的数值数组相同。

一维字符数组的一般形式:

char 数组名[数组长度];

例如:

char c[10];

c[0]='I';c[1]=' ';c[2]='l';c[3]='o';c[4]='v';c[5]='e';c[6]=' ';c[7]='c';

上面定义了一个长度为10的字符数组。具体的存储逻辑结构如图7.6所示。

图7.6 一维字符数组的存储结构

由于字符型数据是以整数(ASCII代码)形式存放的,因此也是可以用整型数组来存放字符数据。

例如:

int c[10];

c[0]='I';c[1]='';c[2]='l';c[3]='o';c[4]='v';

c[5]='e';c[6]='';c[7]='c';c[8]='\0';c[9]='\0';

如上两个事例的结果相同。如果定义的数组长度大于使用的数组长度,则多出的存储空间自动以‘|\0’填充。

 

二维字符数组的一般形式:

char 数组名[行下标值][列下标值];

例如:

char c[2][5];

c[0][0]='I';c[0][1]=' ';c[0][2]='l';c[0][3]='o';c[0][4]='v';

c[1][0]='e';c[1][1]=' ';c[1][2]='c';c[1][3]='\0';c[1][4]='\0';

 

三维字符数组的一般形式:

char 数组名[高下标值][行下标值][列下标值];

例如:

char c[2][2][2];

c[0][0][0]='I';c[0][0][1]='';c[0][1][0]='l';c[0][1][1]='o';

c[1][0][0]='v';c[1][0][1]='e';c[1][1][0]=' ';c[1][1][1]='c';

注:字符数组和整型数组无论一维、二维还是三维其定义形式是一样的。所以在字符数组定义和使用的过程中可以参考整型或实型数组的定义和使用。

 

7.5.2 字符数组的初始化

由于字符数组也可以分为一维数组、二维数组、三维数组等多位数组。所以其初始化也分为多种方式。但是总体差异不大。

一维字符数组初始化:

方式1:

char a[5] = {'a','b','c','d','e'};

方式2:

char a[5];

c[0]='a';

c[1]='b';

c[2]='c';

c[3]='d';

c[4]='e';

定义一个存储空间长度为5的字符数组。还可以用一个循环实现存储。如下:

方式3:

char a[5];

for(int i = 0 ; i<5 ; i++)

{

    a[i] = 'a' + i;

}

循环把'a'+i的值赋值给a[i],实现与上面一样的效果。

 

7.5.3 字符数组的基本使用

在C语言中不像其他的数组元素只能逐个引用,字符数组可以一次引用整个数组。

一维数组引用的一般形式:

数组名[下标值];

说明:

(1) 如果定义的数组长度为N,则下标值的取值范围为0~N-1。

(2) 如果取得下标值大于N-1,称之为下标越界。有些编译器不会检查下标越界问题。 就是说如果下标越界不会报错,而会产生未知的数据,影响程序结果。比如a[N+2]就是指向未知的内存空间。

其实,在上一节一维数组的初始化中我们已经使用了数组的引用。比如初始化的方式2和方式3。再比如:

int a[5]={65,67,68,69,70};

for(i=0; i<5 ; i++)

{

    printf(“%c\n”, a[i]);

}

其中,printf中的a[i]就表示每个下标对应一个元素值。由于整数65到70分别代表对应ASCII码

 

例7.5】输出一个字符串。

解题思路:首先定义一个一维数组,然后给数组中的每一个元素赋初始值,最后循环输出每一个元素的值。

编写程序:

#include <stdio.h>
int main()
{
	char c[20]={'I',' ', 'l', 'o', 'v', 'e', ' ', 'C', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm'};
	int i=0;
	for ( i=0 ; i<20 ; i++ )
	{
		printf("%c", c[i]);
	}
	printf("\n");
	return 0;
}

运行结果:

I love C program

Press any key to continue

 

程序分析:该程序是一个简单的输出程序。程序第4行初始化要输出的数据。程序第6~9行输出结果。

 

添加字符二维数组的代码。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章