- 数组是
一组有序数据的集合
。数组中各数据的排列是有一定规律的,下标代表数据在数组中的序号。 - 用过一个数组名(如s)和下标(如15)来唯一的确定数组中的元素,如s[15]就代表第15个学生的成绩。
- 数组中的每一个元素都
属于同一个数据类型
。不能把不同类型的数据(如学生的成绩和学生的性别)放在同一个数组中。 - 由于计算机键盘只能输入有限的单个字符而无法表示上下标,C语言规定用方括号
[]
中的数字来表示下标,如用s[15]来表示第15个学会的成绩。
第一节 怎样定义和引用一维数组
-
一维数组是数组中最简单的,它的元素只需要用数组名加一个下标,就能唯一的确定。
-
定义一个整型数组,数组名为stud,此数组包含10个整型元素。(使用Visual C++ 会开辟4 * 10 = 40 字节的空间)
int stud[10]
-
定义一维数组的一般形式为
类型说明符 数组名[常量表达式]
。 -
说明:
-
- 数组名的命名规则和变量名相同,遵循标识符命名规则。
- 在定义数组时,需要指定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组的长度。例如,指定stud[10],表示stud数组有10个元素。注意下标是从0开始的,这10个元素是stud[0], stud[1], stud[2], stud[3], stud[4], stud[5], stud[6], stud[7], stud[8], stud[9]。特别注意:按上面的定义,不存在数组元素stud[10]。
- 常量表达式中可以包括常量和符号常量,如int stud[3+5];但不能包含变量,如int stud[n]是不合法的。也就是说,C语言不允许对数组的大小做动态定义,即数组的大小不依赖于程序运行过程中变量的值。
-
-
练习:对10个数组元素依次赋值为0,1,2,3,4,5,6,7,8,9,要求按逆序输出。
-
#include <stdio.h> int main() { int a[10], i; for (i = 0; i<= 9; i++) a[i] = i; for (i = 9; i>= 0; i--) printf("%d ", a[i]); printf("\n"); return 0; }
-
-
在定义数组的同时,给各数组元素赋值,称为数组的
初始化
。-
(1) 在定义数组时,对全部数组元素赋予初值:
```c
int stud [10]={98,99,97,96,95,97,93,90,91,99}
``` 初始化之后,相当于stud[0] =98, stud[1] =99, stud[2] =97, stud[3] =96, stud[4] =95,stud[5] =97, stud[6] =93, stud[7] =90, stud[8] =91, stud[9] =99。
(2)可以只给数组中的一部分元素赋值:
int stud[10] ={98,99,97,96}
定义stud数组有10个元素,但{ }内只提供了4个初值,表示只给前面4个元素赋初始值,系统 自动给后6个元素赋初值为0;相当于stud[0] =98, stud[1] =99, stud[2] =97, stud[3] =96, stud[4] =0,stud[5] =0, stud[6] =0, stud[7] =0, stud[8] =0, stud[9] =0。
数值型
未被赋值的初始化为0
,如果是字符型
数组,则初始化为’\0’
,如果是指针型数组,则初始化为NULL
,即空指针。(3) 如果想使一个数组中全部元素值为0,可以写成
int stud[10] = {0,0,0,0,0,0,0,0,0,0} 或 int stud[10] = {0}
(4) 在对全部数组元素赋初始值时,由于数据的个数已经确定,因此可以不指定数组长度。例如:
int stud[10] = {98,99,97,96,95,97,93,90,91,99} 或 int stud[] ={98,99,97,96,95,97,93,90,91,99}
虽然没有在[ ]指定数组的长度,但是系统会根据花括号中数据的个数确定stud数组有10个元素。
-
-
引用数组元素的方法为:
数组名[下标]
。下标可以是整型常量或整型表达式。- 注意:定义数组时用到的
数组名[常量表达式]
和引用数组元素时用的数组名[下标]
形式相同,但是含义不同,例如:
int a[10]; //前面有int,这是定义数组,指定数组元素包含10个元素 t = a[6];//这里的a[6]表示引用a数组中序号为6的元素
- 注意:定义数组时用到的
-
练习1:对10个数依次赋值为5,6,7,8,9,10,11,12,13,14,逆序打印到屏幕中。
-
#include <stdio.h> int main() { int i, a[10] = {5,6,7,8,9,10,11,12,13,14}; for (i = 9; i >= 0; i--) printf("%d ", a[i]); return 0; }
-
-
练习2:用数组来处理Fibonacci数列问题。
#include <stdio.h> int main() { int fib[40] = { 1,1 }, i; for (i = 2; i < 40; i++) { fib[i] = fib[i - 1] + fib[i - 2]; } for (i = 0; i < 40; i++) printf("%d\t", fib[i]); return 0; }
-
练习3:输入10个地区的面积,要求对它们按由小到大的顺序排列。
#include <stdio.h> int main() { int area[10], i, j, t; printf("输入10个地区的面积:\n"); for (i = 0; i <10; i++) scanf("%d", &area[i]); printf("\n"); for (j = 0; j < 9; j++) //进行9次循环,实现9趟比较 for (i = 0; i < 9 - j; i++) //在每一趟中进行9-j次比较 if (area[i] > area[i + 1]) { t = area[i]; area[i] = area[i + 1]; area[i + 1] = t; } printf("排好的顺序为:\n"); for (i = 0; i < 10; i++) printf("%d ", area[i]); printf("\n"); return 0; }
第二节 怎样定义和引用二维数组
-
二维数组常称为矩阵。把二维数组写成行和列的排列形式,可以有助于形象化地理解二维数组的逻辑结构。
-
二维数组定义的一般形式为:
类型说明符 数组名[常量表达式][常量表达式];
-
一维学生成绩平均值: int stud[10];
-
二维学生成绩:
-
用矩阵形式(如3行4列)表示二维数组,是逻辑上的概念,能形象的表示出行列关系。而在内存中,各元素是连续存放的,不是二维的,是线性的。如:
int stud[10][3];
:10行3列的数组,包含30个int类型的空间float a[3][3];
:3行4列的数组,包含12个float类型的空间float b[5][10];
:5行10列的数组,包含50个float类型的空间
-
-
C语言还允许使用多维数组,例如:
int a[3][4][5]; //三维数组a,有3页,4列,5行,共包含60个int类型的空间
- 多维数组元素在内存中的排列顺序为:第1维的下标变化最慢,最右边的下标变化最快。
-
二维数组的初始化:可以用“初始化列表”对二维数组初始化
-
-
分行给二维数组赋初值,如:
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12} };
这种赋初值方法比较直观,把第1个花括号内的数据赋给第1行的元素,第2个花括号内的数据赋给第2行的元素······即按行赋初值。
-
将所有数据写在一个花括号内,按数组元素在内存中的排列顺序对各元素赋初值,如:
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
缺点:数据多时,则会写成一大片,难以检查,书写容易遗漏。
-
可以对部分元素赋初值,例如:
int a[3][4] = {{1},{5},{9}};
只对各行第1列(序号为0的列)的元素赋初值,其余元素值自动补0。
-
如果对全部元素都赋值(即提供全部初始数据),则定义数组时对第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};
系统会根据数据总个数和第2维的长度算出第1维的长度。
-
-
-
二维数组元素的表示形式为:
数组名[下标][下标]
。在引用数组元素时,下标值应在已定义的数组大小的范围内,例如:int a[3][4]; //定义a为3×4的二维数组 a[3][4] = 3; //不存在a[3][4]元素,a可用的行下标范围为0~2,列下标的范围为0~3
- 注意:应严格区分在定义数组时用的
a[3][4]
和引用元素时的a[3][4]
的区别。
- 注意:应严格区分在定义数组时用的
-
练习:将一个二维数组行和列的元素互换,存到另一个二维数组中
#include <stdio.h> int main() { int a[2][3] = { {1,2,3},{4,5,6} }; int b[3][2], i, j; printf("aray a:\n"); for (i = 0; i <= 1; i++) { for (j = 0; j <= 2; j++) { printf("%d ", a[i][j]); b[j][i] = a[i][j]; } printf("\n"); } printf("array b:\n"); for (i = 0; i <= 2; i++) { for (j = 0; j <= 1; j++) { printf("%d ", b[i][j]); } printf("\n"); } return 0; }
第三节 字符数组
-
字符型数据是以字符的ASCII代码存储在存储单元中的,一般占一个字节。由于ASCII代码也属于整数形式,因此在C99标准中,把字符类型归纳为整型类型中的一种。
-
C语言中没有字符串类型,也没有字符串变量,字符串是存放在字符型数组中的。
-
用来存放字符型数据的数组是字符数组,在字符数组中的一个元素内存放一个字符,如:
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] = {'H', 'e', 'l', 'l', 'o'}; //合法,但浪费存储空间
-
对字符数组初始化,最容易理解的方式是用“初始化列表”,把各个字符依次赋给数组中各元素,例如:
char c[10] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p, 'y'};
把10个数组依次赋给c[0]~c[9]这10个元素。
-
如果在定义字符数组时步进行初始化,则数组中各元素的值是不可预料的。如果
{}
中提供的初值个数(即字符个数)大于数组长度,则出现语法错误。如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余元素自动定为空字符(即’\0’),例如:char c[10] = {'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm'};
-
如果提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数确定数组长度,如:
char c[] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p, 'y'};
数组c的长度自动定为10。用这种方式可以不必人工去数字符的个数,尤其在赋初值的字符个数较多时,比较方便。
-
也可以定义和初始化一个二维字符数组,例如菱形的平面图:
char diamond[5][5] = {{' ', ' ', '*'},{' ', '*', ' ', '*'},{'*', ' ', ' ', ' ', '*'},{' ', '*', ' ', '*'},{' ', ' ', '*'}};
- 代码练习:输出一个已知的字符串
#include <stdio.h> int main() { char c[] = { '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; }
- 代码练习:输出一个菱形:
#include <stdio.h> int main() { char diamond[5][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'
作为结束标志。如果字符数组中存有若干字符,前面9个字符都不是空字符('\0'
),而第10个字符是'\0'
,则认为数组中有一个字符串,其有效字符为9个。也就是说,在遇到字符'\0'
时,表示字符串结束,把它前面的字符组成一个字符串。 -
注意:C系统在用字符数组存储
字符串常量
时会自动加一个'\0'
作为结束符。例如"C program"共有9个
字符。字符串是存放在一维数组中的,在数组中它占10个字节
,最后一个字节'\0'
是由系统自动加上的。 -
在程序中往往依靠检测
'\0'
的位置来判定字符串是否结束,在定义字符数组时应估计实际字符串长度,保证数组长度始终大于字符串实际长度,如果在一个字符数组中先后存放多个不同长度的字符串,则应使数组长度大于最长的字符串的长度。- 说明:
'\0'
代表ASCII码为0的字符,而且不是一个可以显示的字符,而是一个“空操作符”,即它什么也不做。用它来作为字符串结束标志不会产生附加的操作或增加有效字符,只是起一个供辨别的标志。
printf("Hello World\n");
执行此语句,在向内存中存储时,系统自动在最后一个字符
'\n'
的后面加了一个'\0'
,作为字符串结束标志。在执行printf函数时,每输出一个字符检查一次,看下一个字符是否为'\0'
,遇到'\0'
就停止输出。 - 说明:
-
使用字符串常量来使字符数组初始化,如:
char c[10] = {"good time."} char c[] = {"good time."} char str[] = "I am happy";//用字符串作为初值
注意,此时数组str的长度不是10,而是11。因为字符串常量的最后由系统加上一个
'\0'
。char str[] = "I am happy"; <==> char str[] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y', '\0'};//长度为11
char str[] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y'};//长度为10
char c[10] = {"China"};
数组c的前5个元素为:‘C’, ‘h’, ‘i’, ‘n’, ‘a’,第6个元素为
'\0'
,后4个元素也自动设定为空字符,如:
- 字符数组并不要求它的最后一个字符为
'\0'
,甚至可以不包括'\0'
。是否需要加'\0'
,完全根据需要决定。
- 字符数组并不要求它的最后一个字符为
-
字符数组的输入和输出有两种方法:
-
- 逐个字符输入输出。用格式符
"%c"
输入或输出一个字符。 - 将整个字符串一次输入或输出。用
"%s"
格式符,意思是对字符串的输入输出。
- 逐个字符输入输出。用格式符
-
注:遇到
'\0'
就停止输出。 -
说明:
-
(1)输出的字符中不包含结束符
'\0'
。(2)用
"%s"
格式符输出字符串时,printf函数输出项是字符数组名,而不是数组元素名。printf("%s", c[0]);//错误,c[0]不是字符数组名,而是一个数组元素 prntf("%s", c);
(3)如果数组长度大于字符串的实际长度,也只输出
'\0'
前面的数据,例如:char c[10] = {"China"}; //字符串长度为5,连'\0'共占6个字节 printf("%s", c);
只输出字符串的有效字符"China",而不是输出10个字符。
(4)如果一个字符数组中包含一个以上
'\0'
,则遇第一个'\0'
时输出就结束。(5)可以使用scanf函数输入一个字符串,例如:
scanf("%s", c);//C语言中数组名代表该数组第一个元素的地址,不需要再加地址符。
scanf函数中的输入项c是已定义的字符数组名,输入的字符串应短于已定义的字符数组的长度。如果利用一个scanf函数输入多个字符串,则应在输入时以空格分隔。
(6)
printf("%s", c);
实际上是这样执行的:按字符数组名c找到其数组第一个元素的地址,然后逐个输出其中的字符,直到遇到'\0'
为止。#include <stdio.h> int main() { char a[5] = {'h', 'e', 'l', 'l', 'o'}; printf("%s", a); //输出结果:hello烫烫烫台溶^狕S return 0; }
采用%s输出,会出现异常的结尾信息,原因是系统找不到
'\0'
。
-
-
-
使用字符串处理函数:C语言提供了一些用来专门处理字符串的函数,使用方便:
-
-
puts 函数--输出字符串的函数
一般形式为:
puts(字符数组);
。其作用是将一个字符串(以'\0'
结束的字符序列)输出到终端,用puts函数输出的字符串中可以包含转义字符,例如:char str[] = {"China\nBeijing"}; puts(str);
在用puts输出时将字符串结束标志
'\0'
转换成'\n'
,即输出完字符串后换行。 -
gets 函数--输入字符串的函数
一般形式为:
gets(字符数组);
。其作用是从终端输入一个字符串到字符数组,并且得到一个函数值。该函数值是字符数组的起始地址,如执行gets(str);
,从键盘输入Computer回车,将输入的“Computer”送给字符数组str。注意:送给数组共有9个字符(Computer\0),而不是8个字符,返回的函数值是字符数组str的第一个元素的地址。一般利用gets函数的目的是向字符数组输入一个字符串,而不大关心其函数值。注:用puts和gets函数只能输出或输入一个字符串
-
strcat 函数--字符串连接函数
一般形式为:
strcat(字符数组1, 字符数组2);
。其作用是把两个字符数组中的字符串连接起来,把字符串2连接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值----字符数组1的地址,如:char str1[30] = {"People's Republic of"}; char str2[] = {"China"}; printf("%s", strcat(str1, str2));//输出People's Republic of China
注意:字符数组1必须足够大,以便容纳连接后面的新字符串;连接前两个字符串的后面都有
'\0'
,连接时将字符串1后面的'\0'
取消,只在新字符串最后保留'\0'
。 -
strcpy 和 strncpy 函数--字符串复制函数
一般形式为:
strcpy(字符数组1, 字符串2);
。它表示“字符串复制函数”,作用是将字符串2复制到字符数组1中去,如:char str1[10], str2[] = "China"; strcpy(str1, str2);
执行后,str1的状态如下:
说明:(1). “字符数组1”必须足够大,以便容纳“字符串2” 。
(2). “字符数组1”必须写成数组名形式(如str1),“字符串2”可以是字符数组名,也可以是一个字符串常量。
(3). 如果在赋值前未对str1数组初始化或赋值,则str1中各字节中的内容是无法预知的,复制时将str2中的字符串和其后的
'\0'
一起复制到字符数组1中,取代字符数组1中的前面6个字符,最后4个字符并不一定是'\0'
,而是str1中原有的最后4个字节的内容。(4). 不能用赋值语句将一个字符串常量或字符数组直接给一个字符数组。字符数组名是一个地址常量,是不能被改变的值。
(5). 可以用strncpy函数将字符串2中前面n个字符复制到字符数组1中,如:
strncpy(str1, str2, 2);
作用是将str2中最前面2个字符复制到str1中,取代str1中原有的最前面的2个字符。但复制的字符个数n不应多于str1中原有的字符(不包括
'\0'
)。 -
strcmp 函数--字符串比较函数
一般形式为:
strcmp(字符串1, 字符串2);
。其作用是比较字符串1和字符串2,如:strcmp(str1, str2); strcmp("China", "America"); strcmp(str1, "Beijing");
说明:字符串比较的规则是:将两个字符串自左至右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到
'\0'
为止。如全部字符相同,则认为两个字符串相等;若出现不相同的字符,则以第1个对不相同的字符的比较结果为准,在英文字典中位置在后面的为”大“,应注意小写字母比大写字母大(“A” < “B”, “a” > “A”, “Dog” < “cat”)。比较的结果由函数值带回:
(1)如果字符串1与字符串2相同,则函数值为0。
(2)如果字符串1>字符串2,则函数值为一个正整数。
(3)如果字符串1<字符串2,则函数值为一个负整数。
-
strlen 函数--测字符串长度的函数
一般形式为:
strlen(字符数组);
。是测试字符串长度的函数,函数的值为字符串中的实际长度('\0'
不包括在内),例如:char str[10] = "China"; printf("%d", strlen(str)); //5
输出结果不是10,也不是6,而是5。也可以直接测试字符串常量的长度,如:
strlen("China");
-
strlwr 函数--转换为小写的函数
一般形式为:
strlwr(字符串);
。作用是将字符串中大写字母转换成小写字母。 -
strupr 函数--转换为大写的函数
一般形式为:
strupr(字符串);
。作用是将字符串中的小写字母转换成大写字母。
-
-
在使用字符串处理函数时,应当在程序文件的开头用 #include <string.h> 将string.h文件包含到本文件中。
-
练习:输入一行字符,统计其中有多少个单词,单词之间用空格分开
#include <stdio.h> #include <string.h> int main() { char word[100]; int i, num = 0; gets(word); for (i = 0; word[i] != '\0'; i++) { if (word[i] == ' ') num++; } printf("输入的字符%s有%d个单词", word, num+1); return 0; }
-
编写代码实现strcpy函数,将s2中的字符全部复制到s1中。
#include <stdio.h> #include <string.h> int main() { char str1[100], str2[50]; unsigned int i; printf("str1中的原始字符为:%s\n", str1);//乱码 printf("请输入要复制的字符:\n"); gets(str2); for (i = 0; i <= strlen(str2); i++) str1[i] = str2[i]; printf("复制后str1中的字符为%s\n", str2); return 0; }
#include <stdio.h> #include <string.h> int main() { char str1[100], str2[50]; printf("str1中的原始字符为:%s\n", str1);//乱码 printf("请输入要复制的字符:\n"); gets(str2); printf("复制后str1中的字符为%s\n", strcpy(str1, str2)); return 0; }
-