引例:
一个班有30个学生,每个学生有一个成绩,要求这30名学生的平均成绩。
如何表示30个学生成绩?
可以用30个float型白能量进行记录,但是繁琐,没有反映出这些数据间的内在联系
解决方法:
因为是同一类型性质的数据,就可以用同一个名字代表,名字的下标表示这是第几个学生的成绩。一批具有同名同属性的数据就组成一个数组,他们的名字就是数组名。
数组是一组有序数据的集合,数组中各数据的排列是有一定规律的,下标代表数据在数组中的序号。
用数组名和下标即可唯一地确定数组中的元素。
数组中的每一个元素都属于同一个数据类型。
怎样定义和引用一维数组
一维数组是数组中最简单的,它的元素只需要用数组名加一个下标,就能唯一确定。
定义一维数组
类型说明符 数组名[常量表达式]
如果需要使用数组,必须在程序中先定义数组,定义一维数组的一般形式如上所示,例如int a[5];它表示定义了一个整型数组,数组名为a,此数组包含5个整型元素,数组长度为5,这5个元素在数组中存储方式如下图所示,下标从0开始。
数组名的命名规则和变量名相同,遵循标识符命名规则。
在定义数组的时候,需要指定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组长度。如果并未事先指定n的值,就将数组定义为int a[n];则会出现如下报错。如果想要在常量表达式中输入符号常量n,可以加上define预处理,指定n的大小。
常量表达式中可以包括常量和符号常量,不能包含变量。
引用一维数组
数组名[下标]
在定义数组并对其中各元素赋值之后,就可以引用数组中的元素了。只能引用数组元素,而不能一次整体调用整个数组全部元素的值。
数组元素与一个简单变量的地位和作用相似。
“下标”可以是整形常量或整型表达式。
注意:
定义数组时用到的“数组名[常量表达式]”和引用数组元素时用的“数组名[下标]”形式相同,但含义不相同。
int a[10];
//前面有int,这是定义数组,指定数组包含10个元素
t=a[6];
//这里的a[6]表示引用a数组中序号为6的元素
对10个数组元素依次赋值为0,1,2,3,4,5,6,7,8,9,要求按逆序输出
解题思路:
因为有10个元素,所以需要定义一个长度为10的数组。由题知数组中的元素都为整型,所以数组可以定义为整型数组。完成定义之后给数组赋值。
编写程序:
#include <stdio.h>
int main() {
int a[10], i;
for (i = 0; i < 10; i++) //对数组元素赋值
a[i] = i;
for (i = 9; i >= 0; i--) //逆序输出数组元素
printf("%d ", a[i]);
printf("\n");
return 0;
}
运行结果:
一维数组的初始化
为了使程序简洁,常在定义数组的同时给各数组元素赋值,这成为数组的初始化。
(1)在定义数组时对全部数组元素赋予初值,例如:
int a[5]={0,1,2,3,4};
将数组中各元素的初值顺序放在一对花括号中,数据间用逗号隔开。花括号内的数据就称为初始化列表。使用单步运行可以发现经过初始化,数组中元素的状态如下图所示:
(2)可以只给数组中的一部分元素赋值,例如:
int a[5]={0,1,2};
单步运行结果如下图所示:
通过单步运行的结果我们可以发现,如果我们只对数组中一部分元素赋值,那么元素赋值是从下标0开始的,一直到赋值结束,没有被赋值到的元素则默认为值为0。
(3)给数组中全部元素赋值为0,可以有以下两种形式:
int a[5]={0,0,0,0,0};
int a[5]={0}; //未赋值的部分元素自动设定为0
(4)在对全部数组元素赋值时,由于数据的个数已经确定,因此可以不指定数组长度,例如:
int a[]={0,1,2,3,4};
但是上面的写法会让系统默认数组的元素个数为5,如果数组长度与提供初值的个数不相同,则方括号中的数组长度不能省略。
定义和引用二维数组
有3个小分队,每队有6名队员,要把这些队员的工资用数组保存起来以备检查。如下图:
队员1 队员2 队员3 队员4 队员5 队员6 1分队 2456 1847 1243 1600 2346 2757 2分队 3045 2018 1725 2020 2458 1436 3分队 1427 1175 1046 1976 1477 2018
二维数组常称为矩阵。把二维数组写成行和列的排列形式,如上图,可以有助于形象化地理解二维数组的逻辑结构。
定义二维数组
类型说明符 数组名[常量表达式][常量表达式]
二维数组的一般形式如上所示,例如:
float a[3][4],b[5][10];
定义a为3*4(3行4列)的数组,b为5*10的数组。
数组在计算机中如上所示,由上图可以发现,我们可以将二维数组分解成一维数组,如果二维数组a有i行j列,那么我们可以将a分解成i个一维数组,每一个一维数组里面都有j个元素。这样我们就可以将a[i]看做是一维数组的数组名。
注意:
用矩阵形式表示二维数组只是为了表示形象,实际上在内存中,各元素是连续存放的,是线性的。在二维数组中元素排列的顺序是按行存放的。
引用二维数组
数组名[下标][下标]
“下标”可以是整形常量或整型表达式。 数组元素可以出现在表达式中,也可以被赋值,例如:
b[1][2]=a[2][3]/2;
注意:
在引用数组元素时,下标值应在已定义的数组大小的范围内。同一维数组一样,二维数组的下标同样是同0开始的,所以如果是i*j的二维数组,那么它下标最小为[0][0],最大为[i-1][j-1]。
二维数组的初始化
(1)分行给二维数组赋初值,例如:
int a[2][3]={{0,1,2},{3,4,5}};
赋值结果:
这种赋值方法比较直观,一个花括号对应一行。
(2)可以将所有数据写在一个花括号里面,按数组元素在内存中的排列顺序对各元素赋值,例如:
int a[2][3]={0,1,2,3,4,5};
赋值结果与之前相同,但是这种方式不直观,容易遗漏,也不易检查。
(3)可以对部分元素赋值,例如:
int a[2][3]={{1},{3}};
赋值结果:
通过赋值结果可以发现,同一维数组一样,除了被赋值元素以外,其余元素皆默认值为0。因为一个花括号对应一行,所以上面数组的赋值是对每一行从头开始赋值。
(4)如果对全部元素进行赋值,则定义数组时,对行数不必指定,但是对列数一定要指定。例如:
int a[][3] = { 0,1,2,3,4,5 };
赋值结果:
在定义的时候也可以省略部分元素的赋值,但是赋值的时候一定要分行赋值,即有几行就在里面分几个花括号,例如:
int a[][3] = { {1,2,3},{3} };
赋值结果:
但是不可以省略列数。
字符数组
字符型数据是以字符的ASCII代码存储在存储单元中的,一般占一个字节。由于ASCII代码也属于整数形式,因此字符类型也可以归类于整型类型中。
定义字符数组
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'; //合法,但浪费存储空间
字符数组的初始化
对字符数组初始化,最容易理解的方式是用“初始化列表”,把各个字符依次赋给数组中各元素。
如果在定义字符数组时不进行初始化,则数组中各元素的值是不可预料的。
如果花括号中提供的初值个数(即字符个数)大于数组长度,则出现语法错误。
如果初值个数小于数组长度,则只将这些字符赋给数组中从头开始的顺序的元素,其余的元素自动定位空字符('\0')。例如:
char c[10] = { 'c',' ','p','r','o','g','r','a','m'};
赋值结果:
如果提供的初值个数与预定的数组长度相同,在定义的时候可以省略数组长度,系统会自动根据初值个数确定数组长度。例如:
char c[] = { 'c',' ','p','r','o','g','r','a','m'};
//数组c的长度自动定为10
引用字符数组中的元素
输出一个已知的字符串。
解题思路:
先定义一个字符数组,并用“初始化列表”对其赋值,然后逐个循环输出此字符数组中的字符。
编写程序:
#include <stdio.h>
int main()
{
char c[15]={'I',' ','a','m',' ','a',' ','s','t','u','d','e','n','t','.'};
int i;
for(i=0;i<15;i++)
printf("%c",c[i]);
printf("\n");
return 0;
}
运行结果:
输出一个菱形图
解题思路:
每行包括5个字符,其中有的是空白字符,有的是'*'字符,记下在每行中'*'出现的位置。定义一个字符型的二维数组,用“初始化列表”进行初始化。这样字符数组中已存放一个菱形图案,然后用嵌套的for循环输出字符数组中所有元素。
编写程序:
#include <stdio.h>
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系统在用字符数组存储字符串常量的时候会自动加一个'\0'作为结束符
在定义字符数组时应该估计实际字符串长度,保证数组长度始终大于字符串实际长度。
如果在一个字符数组中先后存放多个不同长度的字符串,则应使数组长度大于最大的字符串的长度。
printf("How do you do?\n");
/*在向内存中存储时,系统自动在最后一个字符′\n′的后面加了一个′\0′,作为字符串结束标志。
在执行printf函数时,每输出一个字符检查一次,看下一个字符是否为′\0′,遇′\0′就停止输出。*/
char c[]={"I am happy"};
/*或*/
char c[]="I am happy";
用一个字符串(注意字符串的两端是用双引号而不是单引号括起来的)作为字符数组的初值。
注意:
数组c的长度不是10,而是11。因为字符串常量的最后由系统加上一个′\0′。
char c[]={′I′, ′ ′, ′a′,′m′, ′ ′,′h′,′a′,′p′,′p′,′y′,′\0′};
//不等于
char c[]={′I′, ′ ′, ′a′,′m′, ′ ′,′h′,′a′,′p′,′p′,′y′};
char c[10]={"China"};
//数组c的前5个元素为: ′C′,′h′,′i′,′n′,′a′,第6个元素为′\0′,后4个元素也自动设定为空字符。
字符数组的输入输出
(1) 逐个字符输入输出。用格式符“%c”输入或输出一个字符。
(2) 将整个字符串一次输入或输出。用“%s”格式符,意思是对字符串的输入输出。
说明:
(1)输出的字符中不包括结束符'\0'
(2)用“%s”格式符输出字符串时,printf函数中的输出项是字符数组名,而不是数组元素名。
(3)如果数组长度大于字符串的实际长度,也只输出到遇'\0'结束。
(4)如果一个字符数组中包含一个以上'\0',则遇第一个'\0'时输出就结束。
(5)可以用scanf函数输入一个字符串。如果利用一个scanf函数输入多个字符串,则应在输入时以空格分隔。
注意:
scanf函数中的输入项如果是字符数组名,不要再加地址符&,因为在C中数组名代表的是该数组第一个元素的地址。
使用字符串处理函数
输出字符串的函数
puts(字符数组)
作用:
将一个字符串(以'\0'结束的字符序列)输出到终端
用puts函数输出的字符串中可以包含转义字符
在用puts输出时将字符串结束标志'\0'转换成'\n',即输出完字符串后换行。
#include <stdio.h>
int main()
{
char str[]={"China\nBeijing"};
puts(str);
return 0;
}
输入字符串的函数
gets(字符数组)
作用:
从终端输入一个字符串到字符数组,并且得到一个函数值。该函数值是字符数组的起始地址。
如果从键盘输入: Computer
将输入的字符串″Computer″送给字符数组str(请注意,送给数组的共有9个字符,而不是8个字符),返回的函数值是字符数组str的第一个元素的地址。
注意:
用puts和gets函数只能输出或输入一个字符串。
字符串连接函数
strcat(字符数组1,字符数组2)
作用:
把两个字符数组中的字符串连接起来,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值——字符数组1的地址。
字符数组1必须足够大,以便容纳连接后的新字符串。
连接前两个字符串的后面都有′\0′,连接时将字符串1后面的′\0′取消,只在新串最后保留′\0′。
#include <stdio.h>
#include <string.h>
int main() {
char str1[30] = { "People′s Republic of " };
char str2[] = { "China" };
printf("%s", strcat(str1, str2));
return 0;
}
字符串复制函数
strcpy(字符数组1,字符数组2)
作用:
将字符串2复制到字符数组1中去。
字符数组1必须定义得足够大,以便容纳被复制的字符串2。字符数组1的长度不应小于字符串2的长度。
“字符数组1”必须写成数组名形式,“字符串2”可以是字符数组名,也可以是一个字符串常量。
若在复制前未对字符数组1初始化或赋值,则其各字节中的内容无法预知,复制时将字符串2和其后的′\0′一起复制到字符数组1中,取代字符数组1中前面的字符,未被取代的字符保持原有内容。
不能用赋值语句将一个字符串常量或字符数组直接给一个字符数组。字符数组名是一个地址常量,它不能改变值,正如数值型数组名不能被赋值一样。
可以用strncpy函数将字符串2中前面n个字符复制到字符数组1中去。 将str2中最前面n个字符复制到str1中,取代str1中原有的最前面n个字符。但复制的字符个数n不应多于str1中原有的字符(不包括′\0′)。
字符串比较函数
strcmp(字符串1,字符串2)
作用:
比较字符串1和字符串2。
字符串比较的规则是: 将两个字符串自左至右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到′\0′为止。
(1) 如全部字符相同,则认为两个字符串相等;
(2) 若出现不相同的字符,则以第1对不相同的字符的比较结果为准。
比较的结果由函数值带回。
(1) 如果字符串1与字符串2相同,则函数值为0。
(2) 如果字符串1>字符串2,则函数值为一个正整数。
(3) 如果字符串1<字符串2,则函数值为一个负整数。
注意:
对两个字符串比较不能直接用str1>str2进行比较,因为str1和str2代表地址而不代表数组中全部元素,而只能用 (strcmp(str1,str2)>0)实现,系统分别找到两个字符数组的第一个元素,然后顺序比较数组中各个元素的值。
#include <stdio.h>
#include <string.h>
int main() {
char str1[80] = "string";
char str2[80] = "compare";
if (strcmp(str1, str2) > 0)
printf("yes\n");
else printf("no\n");
if (strcmp("China","Korea") > 0)
printf("yes\n");
else printf("no\n");
if (strcmp(str1,"Beijing") > 0)
printf("yes\n");
else printf("no\n");
return 0;
}
测字符串长度的函数
strlen(字符数组)
作用:
测试字符串长度的函数。函数的值为字符串中的实际长度(不包括′\0′在内)。
#include <stdio.h>
#include <string.h>
int main()
{
char str[10]="China";
printf("%d,%d\n",strlen(str),strlen("China"));
}
转换为大小写的函数
strlwr(字符串)
作用:
将字符串中大写字母换成小写字母。
strupr(字符串)
作用:
将字符串中小写字母换成大写字母。
注意:
以上介绍了常用的8种字符串处理函数,它们属于库函数。
库函数并非C语言本身的组成部分,而是C语言编译系统为方便用户使用而提供的公共函数。不同的编译系统提供的函数数量和函数名、函数功能都不尽相同,使用时要小心,必要时查一下库函数手册。
在使用字符串处理函数时,应当在程序文件的开头用#include <string.h>把string.h文件包含到本文件中。