(1)数组是一组有序数据的集合。 数组中各数据的排列是有一定规律的,下标代表数据在数组中的序号。
(2)用一个数组名和小标来唯一地确定数组中的元素。
(3)数组中的每一个元素都属于同一个数据类型。不能把不同类型的数据放在同一个数组中。
由于计算机键盘只能输入有限的单个字符而无法表示上下标,C语言规定用方括号中的数字来表示下标。
定义一维数组的一般形式为
类型说明符 数组名[常量表达式]
如:int a[10]
它表示定义了一个整形数组,数组名为a,此数组包含10个整形元素。相当于定义了10个简单的整型变量。
说明:
(1)数组名的命名规则和变量名相同,遵循标识符命名规则。
(2)在定义数组时,需要指定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组长度。请特别注意,数组元素的下标从0开始,如用“int a[10];”定义数组,则最大下标值为9,不存在数组元素a[10]。
(3) 常量表达式中可以包括常量和符号常量,不能包含变量。如“int a[3+5];”合法,但“int a[n];”就是不合法的。也就是说,C语言不允许对数组的大小做动态定义,即数组的大小不依赖于程序运行过程中变量的值。
引用数组元素的表示形式为
数组名[下标]
说明:
(1)只能引用数组元素而不能一次整体调用整个数组全部元素的值。
(2)数组元素与一个简单变量的地位和作用相似。
(3)“下标”可以是整型常量或整型表达式。
注意: 定义数组时用到的“数组名[常量表达式]” 和引用数组元素时用的“数组名[下标]”形式相同,但含义不同。如:
int a[10]; //前面有int,这是定义数组,指定数组包含10个元素 t=a[6]; //这里的a[6]表示引用a数组中序号为6的元素
举个例子【例6.1】对10个数组元素依次赋值为0,1,2,3,4,5,6,7,8,9,要求按逆序输出。
编写代码
#include <iostream> //对10个数组元素依次赋值为0,1,2,3,4,5,6,7,8,9,要求按逆序输出。 int main() { int i,a[10],t; for(i=0;i<10;i++){ //对数组元素a[0]~a[9]赋值 a[i]=i; } for(i=9,t=0;i>=0;i--,t++){ //输出a[9]~a[0]共10个数组元素 printf("a[%d]=%d\t",t,a[i]); } return 0; }
运行结果
**程序分析:**第1个for循环使a[0]~a[9]的值为0~9,见下图。第2个for循环按a[9]~a[0]的顺序输出各元素的值。
为了使程序简洁,常在定义数组的同时给各数组元素赋值,这称为数组的初始化。
(1) 在定义数组时对全部数组元素赋予初值。如:
int a[10]={0,1,2,3,4,5,6,7,8,9};
将数组中各元素的初值顺序放在一对花括号内,数据间用逗号分隔。花括号内的数据就称为“初始化列表”。
(2) 可以只给数组中的一部分元素赋值。如:
int a[10]={0,1,2,3,4};
定义a数组有10个元素,但花括号内只提供5个初值,这表示只给前面5个元素赋初值,系统自动给后5个元素赋初值为0。
(3) 给数组中全部元素赋初值为0。可以写成:
int a[10]={0,0,0,0,0,0,0,0,0,0}; //或 int a[10]={0}; //未赋值的部分元素自动设定0
(4) 在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度。如:
int a[5]={1,2,3,4,5}; //可以写成 int a[ ]={1,2,3,4,5};
但是,如果数组长度与提供初值的个数不相同,则方括号中的数组长度不能省略。
说明: 如果在定义数值型数组时,指定了数组的长度并对之初始化,凡未被“初始化列表”指定初始化的数组元素,系统会自动把它们初始化为0(如果是字符型数组,则初始化为’\0’,如果是指针型数组,则初始化为NULL,即空指针)。
【例6.2】用数组来处理求Fibonacci数列问题。
编写代码
#include <stdio.h> //用数组来处理求Fibonacci数列问题。即,兔子繁殖问题 int main() { int i; int f[20]={1,1}; //对最前面两个元素f[0]和f[1]赋初值1 for(i=2;i<20;i++){ f[i]=f[i-1]+f[i-2]; //先后求出f[2]~f[19]的值 } for(i=0;i<20;i++){ printf("%d\t",f[i]); //输出一个数 if((i+1)%5==0) printf("\n"); //控制每输出5个数后换行 } return 0; }
运行结果
【例6.3】有10个地区的面积,要求对它们按由小到大的顺序排列。
算法用:起泡排序法
编写代码
#include <stdio.h> //有10个地区的面积,要求对它们按由小到大的顺序排列。 int main() { int s[10]; int i,j,t; printf("please enter 10 numbers:\n"); for (int i = 0; i < 10; i++) { scanf("%d",&s[i]); } for(j=0;j<(10-1);j++){ //进行9次循环,实现9趟比较 for (int i = 0; i < ((10-1)-j); i++) { //在每一趟比较中,实现9-j次比较 if(s[i]>s[i+1]){ //相邻两个数进行比较 t=s[i]; s[i]=s[i+1]; s[i+1]=t; } } } printf("the sorted numbers:\n"); for (int i = 0; i < 10; i++) { printf("%d\t",s[i]); } return 0; }
运行结果
二维数组常称为矩阵 (matrix)。把二维数组写成行(row)和列(column)的排列形式,可以有助于形象化地理解二维数组的逻辑结构。
二维数组的一般形式
类型说明符 数组名 [常量表达式] [常量表达式] ;
例如:
float a[3][4], b[5][10]; //定义a为3×4(3行4列)的数组,b为5×10(5行10列)的数组
二维数组可被看作一种特殊的一维数组: 它的元素又是一个一维数组。 例如,
float a[3][4];
可以把a看作一个一维数组,它有3个元素:a[0], a[1], a[2],每个元素又是一个包含4个元素的一维数组:
a[0] —— a[0][0] a[0][1] a[0][2] a[0][3] a[1] —— a[1][0] a[1][1] a[1][2] a[1][3] a[2] —— a[2][0] a[2][1] a[2][2] a[2][3]
C语言中,二维数组中元素排列的顺序是按行存放的,即在内存中先顺序存放第1行的元素,接着在存放第2行的元素···
注意: 用矩阵形式表示二维数组,是逻辑上的概念,而在内存中,各元素是连续存放的,不是二维的,是线性的。
C语言还允许使用多维数组。多维数组元素在内存中的排列顺序为: 第1维的下标变化最慢,最右边的下标变化最快。
二维数组元素的表示形式为
数组名 [下标] [ 下标]
说明:(1)“下标”可以是整型常量或整型表达式。
(2)数组元素可以出现在表达式中,也可以被赋值,如:b[1][2]=a[2][3]/2;
注意:在引用数组元素时,下标值应在已定义的数组大小的范围内。
可以用“初始化列表”对二维数组初始化。
(1)分行给二维数组赋初值。(最清楚直观)
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
(2)可以将所有数据写在一个花括号内,按数组元素在内存中的排列顺序对各元素赋初值。
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
(3)可以对部分元素赋初值。
int a[3][4]={{1},{5},{9}}; //(1) int a[3][4]={{1},{0,6},{0,0,11}}; //(2) int a[3][4]={{1},{5,6}}; //(3) int a[3][4]={{1},{},{9}}; //(4)
(4)如果对全部元素都赋初值(即提供全部初始数据),则定义数组时对第1维的长度可以不指定,但第2维的长度不能省。
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; //等价于 int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12};
在定义时也可以只对部分元素赋初值而省略第1维的长度,但应分行赋初值。
int a[][4]={{0,0,3},{},{0,10}};
【例6.4】将一个二维数组行和列的元素互换,存到另一个二维数组中。例如:
编写程序
#include <stdio.h> //将一个二维数组行和列的元素互换,存到另一个二维数组中。 int main(){ int a[2][3]={{1,2,3},{4,5,6}}; int b[3][2],i,j; printf("array a:\n"); for (int i = 0; i < 2; i++) { //处理a数组中的一行中各元素 for (int j = 0; j < 3; j++) { //处理a数组中某一列中各元素 printf("%d\t",a[i][j]); //输出a数组的一个元素 b[j][i]=a[i][j]; //将a数组元素的值赋给b数组相应元素 } printf("\n"); } printf("array b:\n"); //输出b数组各元素 for(j=0;j<3;j++){ //处理b数组中一行中各元素 for(i=0;i<2;i++){ //处理b数组中一列中各元素 printf("%d\t",b[j][i]); //输出b数组的一个元素 } printf("\n"); } return 0; }
运行结果
用来存放字符数据的数组是字符数组。在字符数组中的一个元素内存放一个字符。
定义字符数据的数组和定义数值型数组的方法类似。例如:
char c[10]; c[0]='I'; c[1]=' ';c[2]='a';c[3]='m';c[4]=' '; c[5]='h'; c[6]='a'; c[7]='p'; c[8]='p'; c[9]='y';
由于字符型数据是以整数形式(ASCII代码)存放的,因此也可以用整型数组来存放字符数据。如:
int c[10]; c[0]='a'; //合法,但浪费存储空间
对字符数组初始化,最容易理解的方式是用“初始化列表”,把各个字符依次赋给数组中各元素。
char c[10]={′I′,′ ′ ,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′}; //把10个字符依次赋给c[0]~c[9]这10个元素
如果在定义字符数组时不进行初始化,则数组中各元素的值是不可预料的。
如果花括号中提供的初值个数(即字符个数)大于数组长度,则出现语法错误。
如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定为空字符(即′\0′)。
char c[20]={′c′,′ ′,′p′,′r′,′o′,′g′,′r′,′a′,′m′};
如果提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数确定数组长度
char c[]={′I′,′ ′,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′}; //数组c的长度自动定为10
也可以定义和初始化一个二维字符数组。
char diamond[5][5]={{′ ′,′ ′,′*′},{′ ′,′*′,′ ′,′*′},{′*′,′ ′,′ ′,′ ′,′*′},{′ ′,′*′,′ ′,′*′},{′ ′,′ ′,′*′}};
【例 6.5】输出一个已知的字符串。
编写程序
#include <iostream> //输出一个已知的字符串。 int main() { char c[15]={'I',' ','a','m',' ','a',' ','s','t','u','d','e','n','t','.'}; int i; printf("array c:\n"); for(i=0;i<15;i++){ printf("%c",c[i]); } printf("\n"); return 0; }
运行结果
【例 6.6】输出一个菱形图。
编写程序
#include <iostream> //输出一个菱形图。 int main() { char diamond[][5]={{' ',' ','*'},{' ','*',' ','*'},{'*',' ',' ',' ','*'}, {' ','*',' ','*'},{' ',' ','*'}}; int i,j; for(i=0;i<5;i++){ for(j=0;j<5;j++){ printf("%c",diamond[i][j]); } printf("\n"); } return 0; }
运行结果
在C语言中,是将字符串作为字符数组来处理的。
在实际工作中,人们关心的往往是字符串的有效长度而不是字符数组的长度。
为了测定字符串的实际长度,C语言规定了一个“字符串结束标志”,以字符′\0′作为结束标志。
″C program″ 字符串是存放在一维数组中的,占10个字节,字符占9个字节,最后一个字节′\0′是由系统自动加上的。
注意:
(1)C系统在用字符数组存储字符串常量时会自动加一个′\0′作为结束符。
(2)在定义字符数组时应估计实际字符串长度,保证数组长度始终大于字符串实际长度。
(3)如果在一个字符数组中先后存放多个不同长度的字符串,则应使数组长度大于最长的字符串的长度。
字符数组的输入输出可以有两种方法。
(1)逐个字符输入输出。用格式符“%c”输入或输出一个字符。
(2)将整个字符串一次输入或输出。用“%s”格式符,意思是对字符串(string)的输入输出。
说明:
(1) 输出的字符中不包括结束符′\0′。
(2) 用“%s”格式符输出字符串时,printf函数中的输出项是字符数组名,而不是数组元素名。
(3) 如果数组长度大于字符串的实际长度,也只输出到遇′\0′结束。
(4) 如果一个字符数组中包含一个以上′\0′,则遇第一个′\0′时输出就结束。
(5)可以用scanf函数输入一个字符串。
1.puts函数——输出字符串的函数
其一般形式为:puts(字符数组)
作用:将一个字符串(以′\0′结束的字符序列)输出到终端。
用puts函数输出的字符串中可以包含转义字符。
在用puts输出时将字符串结束标志′\0′转换成′\n′,即输出完字符串后换行。
2. gets函数——输入字符串的函数
其一般形式为:gets(字符数组)
作用:从终端输入一个字符串到字符数组,并且得到一个函数值。该函数值是字符数组的起始地址。
注意: 用puts和gets函数只能输出或输入一个字符串。
3. strcat函数——字符串连接函数
其一般形式为:strcat(字符数组1,字符数组2)
作用:把两个字符数组中的字符串连接起来,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值——字符数组1的地址。
字符数组1必须足够大,以便容纳连接后的新字符串。
连接前两个字符串的后面都有′\0′,连接时将字符串1后面的′\0′取消,只在新串最后保留′\0′。
4. strcpy和strncpy函数——字符串复制函数
其一般形式为:strcpy(字符数组1,字符串2)
作用:将字符串2复制到字符数组1中去。
说明:
1.字符数组1必须定义得足够大,以便容纳被复制的字符串2。字符数组1的长度不应小于字符串2的长度。
2. “字符数组1”必须写成数组名形式,“字符串2”可以是字符数组名,也可以是一个字符串常量。
3.若在复制前未对字符数组1初始化或赋值,则其各字节中的内容无法预知,复制时将字符串2和其后的′\0′一起复制到字符数组1中,取代字符数组1中前面的字符,未被取代的字符保持原有内容。
4.不能用赋值语句将一个字符串常量或字符数组直接给一个字符数组。字符数组名是一个地址常量,它不能改变值,正如数值型数组名不能被赋值一样。
5. 可以用strncpy函数将字符串2中前面n个字符复制到字符数组1中去。
将str2中最前面2个字符复制到str1中,取代str1中原有的最前面2个字符。但复制的字符个数n不应多于str1中原有的字符(不包括′\0′)。
5. strcmp函数——字符串比较函数
其一般形式为:strcmp(字符串1,字符串2)
作用:比较字符串1和字符串2。
字符串比较的规则是: 将两个字符串自左至右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到′\0′为止。
(1) 如全部字符相同,则认为两个字符串相等;
(2) 若出现不相同的字符,则以第1对不相同的字符的比较结果为准。
比较的结果由函数值带回。
(1) 如果字符串1与字符串2相同,则函数值为0。
(2) 如果字符串1>字符串2,则函数值为一个正整数。
(3) 如果字符串1<字符串2,则函数值为一个负整数。
6. strlen函数——测字符串长度的函数
其一般形式为:strlen(字符数组)
作用:测试字符串长度的函数。函数的值为字符串中的实际长度(不包括′\0′在内)。
7. strlwr函数——转换为小写的函数
作用:将字符串中大写字母换成小写字母。
其一般形式为:strlwr(字符串)
8. strupr函数——转换为大写的函数
其一般形式为:strupr(字符串)
作用:将字符串中小写字母换成大写字母。
注意:
以上介绍了常用的8种字符串处理函数,它们属于库函数。库函数并非C语言本身的组成部分,而是C语言编译系统为方便用户使用而提供的公共函数。不同的编译系统提供的函数数量和数名、函数功能都不尽相同,使用时要小心,必要时查一下库函数手册。
在使用字符串处理函数时,应当在程序文件的开头用#include <string.h>把string.h文件包含到本文件中。