数组
数组的定义
-
语法:类型 数组名[元素个数] (方括号内只能是常量或者常量表达式)
int a[6];
char b[24];
double c[3];
-
上面几个类型分别占用内存的字节数为:
int a[6] ==> 4 * 6 = 24
char b[24] ==> 1 * 24 = 24
double c[3] ==> 8 * 3 = 24
数组不能动态定义
C语言不允许在程序运行过程中修改数组空间大小
访问数组中的元素
-
语法: 数组名[下标]
a[0]; //访问a数组中的第一个元素
b[1]; //访问b数组中的第二个元素
c[5]l //访问c数组中的第六个元素
-
注意:
- int a[5]; //创建一个具有五个元素的数组
- a[0]; //访问第一个元素,第一个元素下标为0,不是1
- a[5]; //报错, 第五个元素下标为a[4]
循环跟数组的关系
-
实现一个执行10次的循环,我们通常是这么写的:
for (i = 0; i < 10; i++) {... }
-
而不是这么写:
for (i = 1; i <= 10; i++) {... }
-
这是因为我们常常需要使用循环来访问数组:
int a[10]; for (i = 0; i < 10; i++) {a[i] = i; }
-
举个例子,我们尝试用数组存放班里5位同学的数学成绩,并计算出平均数。
#include<stdio.h> #define NUM 5 //常量宏定义,可以在此直接修改存放的学生个数 int main() {int s[NUM]; //学生数组int i, sum = 0;for (i = 0; i < NUM; i++) //循环次数为学生个数{printf("请输入第%i位同学的成绩:",i+1); scanf("%d",&s[i]); //将输入的成绩存放到对应的数组下标sum += s[i];}printf("成绩录入完毕,该次考试的平均分是:%.2f\n", (double)sum / NUM ); //计算结果并转换为浮点型return 0; }
数组的初始化
在定义数组的同时给它赋值,称为数组的初始化
方式为:
-
将数组中所有元素初始化为0,可以这么写:
- int a[10] = {0}; //事实上这里只是将第一个元素赋值为0
-
如果是赋予不同的值,那么用逗号隔开即可:
- int a[10] = {1,2,3,4,5,6,7,8,9,0};
-
也可以只给一部分元素赋值,未被赋值的元素自动初始化为0:
-
int a[10] = {1,2,3,4,5,6};
// 表示为前边6个元素赋值,后边4个元素系统自动初始化为0
-
-
有时还可以偷懒,可以只给出各个元素的值,而不指定数组的长度(因为编译器会根据值的个数自动判断数组的长度):
- int a[] = {1,2,3,4,5,6,7,8,9,0};
-
C99增加了一种新特性:指定初始化的元素。这样就可以只对数组中的某些指定元素进行初始化,而未被赋值的元素自动初始化为0:
- int a[10] = {[3]=3,[5]=5,[8]=8};
-
使用sizeof可以计算数组占用内存的大小
int a[10]={0}; printf("%d\n",sizeof(a));
结果
40 // int有4个字节,4*10=40
字符数组
-
字符串常量: "Hello","ABC"
-
字符数组:
char str[10]; str[0] = 'H'; str[1] = 'e'; str[2] = 'l'; str[3] = 'l'; str[4] = 'o'; str[5] = '\0'; ...
字符串处理函数
请参考:C语言标准函数库分类
获取字符串长度
- strlen函数,用于获取字符串的长度: (注意:获取的是长度,不是尺寸)
#include<stdio.h>
#include<string.h> //头文件
int main()
{char str[] = "Hello!";printf("sizeof str = %d\n",sizeof(str));printf("strlen str = %u\n",strlen(str));return 0;
}
运行结果
sizeof str = 7 \\包含'\0'
strlen str = 6 \\字符的个数,不包含'\0'
拷贝字符串
strcpy和strncpy函数
- strcpy函数用于拷贝字符串,包含最后的结束符 '\0'
-
语法:strcpy(目标字符串,源字符串)
注意:
- 为了避免溢出,必须确保用于存放的数组长度足以容纳待拷贝的字符串(注意:长度需要包含结束符 '\0')。
- 源字符串和目标数组的位置不应该重叠。
#include<stdio.h>
#include<string.h> //头文件
int main()
{char str1[] = "Original String"; //长度15char str2[] = "New String"; //长度10char str3[100];strcpy(str1, str2); //将str2中的字符(包括'\0')拷贝到str1strcpy(str3, "Copy Successful");printf("str1: %s\n",str1);printf("str2: %s\n",str2);printf("str3: %s\n",str3);return 0;
}
运行结果
str1: New String //拷贝完后,str1的长度改变了,为10.但sizeof没有变化,仍为16
str2: New String
str3: Copy Successful
若源字符串长度大于目标字符串:
char str1[] = "Original String"; //长度15
char str2[] = "New String"; //长度10
strcpy(str2, str1);
printf("str1: %s\n",str1);
printf("str2: %s\n",str2);
运行结果
str1: ring
str2: Original String
拷贝时,源字符串过长,目标字符串溢出,导致拷贝后源字符串发生变化。
因此在程序实现拷贝时,应该限制源字符串的长度,确保目标字符串在执行完拷贝后不会发生溢出
-
strncpy函数
- 和 strcpy函数一样,strncpy(dest, src, n) 函数将拷贝源字符串的 n 个字符到目标数组中。如果源字符串的长度小于 n,那么就用 '\0' 填充额外的空间。如果源字符串的长度大于或等于 n,那么只有 n 个字符被拷贝到目标数组中(注意:这样的话将不会以结束符 '\0' 结尾)。
- 提示:为了使该函数更“安全”,建议使用dest[sizeof(dest) - 1] = '\0'; 语句确保目标字符串是以 '\0' 结尾
- 源字符串和目标数组的位置不应该重叠。
#include<stdio.h> #include<string.h> //头文件 int main() {char str1[] = "To be or not to be"; char str2[40]; strncpy(str2, str1,5); //拷贝str1中5个字符到str2中str2[5] = '\0'; //追加结束字符printf("str2: %s\n",str2);return 0; }
运行结果
str2: To be
注意:如果源字符串的长度大于或等于 n,在拷贝完后需要对目标字符串追加结束符,否则结果会乱码
连接字符串
-
连接字符串: strcat和strncat
连接字符串用于将一个字符串连接到另一个字符串的后边,也叫字符串的拼接。(注意:目标数组里必须包含一个字符,可以是空字符)
-
strcat -- 连接字符串
-
strcat函数用于连接两个字符串。
-
将源字符串拷贝并连接到目标数组存放的字符串后边,此过程将覆盖第一个参数的结束符 '\0'。
-
两个参数的位置不应该重叠
#include<stdio.h> #include<string.h> //头文件 int main() {char str1[] = "I love"; char str2[] = "this world!"; strcat(str1," "); //在str1末尾连接一个空格strcat(str1, str2); //将str2连接到str1末尾printf("str1: %s\n",str1);return 0; }
运行结果
str1: I love this world!
-
-
strncat -- 连接字符串(受限)
- strncat函数用于拷贝源字符串中的 n 个字符到目标数组的字符串后边,并在末尾添加结束符 '\0'。
- 如果源字符串的长度小于 n,那么不会像strncpy函数那样使用 '\0' 进行填充(但结束符 '\0' 还是有的)。
- 另外,目标数组中的原有的字符串并不算在 n 中
比较字符串
-
比较字符串: strcmp和strncmp
比较字符串用于比较两个字符串是否完全一致。如果一致,则返回0;如果不一致,那么根据情况,返回大于0或小于0的值。(从第一个字符开始,依次对比两个字符串每一个字符的ASCII码。如果第一个字符串ASCII码小于第二个字符串对于的字符,则返回一个小于0的值,反之则相反。)
-
strcmp -- 比较字符串
strcmp函数用于比较两个字符串。
该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,直到发现两个字符不相等或抵达结束符('\0')为止
#include<stdio.h> #include<string.h> //头文件 int main() {char str1[] = "Hello World!"; char str2[] = "Hello World"; if (!strcmp(str1,str2)) //比较两个字符串返回值是否等于0{printf("两个字符串完全一致!\n");}else{printf("两个字符串存在差异!\n");}return 0; }
运行结果
两个字符串完全一致!
-
strncmp -- 比较字符串(受限)
strncmp函数用于比较两个字符串的前 n 个字符。
该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,发现两个字符不相等或抵达结束符('\0')为止,或者前 n 个字符完全一样,也会停止比较。
二维数组
如果说一维数组是一条线,那么二维数组就可以说是一个面。二维数组也称为矩阵
科目\学号 | 1 | 2 | 3 | 4 | 5 | ... |
---|---|---|---|---|---|---|
语文 | 80 | 92 | 85 | 86 | 99 | |
数学 | 78 | 65 | 89 | 70 | 99 | |
英语 | 67 | 78 | 76 | 89 | 99 | |
综合 | 88 | 68 | 98 | 90 | 99 |
上表是一个记录了各个同学各个科目的成绩。对于上表,如果使用一维数组,则需分别定义4个数组
int chinese[50];
int math[50];
int english[50];
int comprehensive[50];
但如果使用二维数组,我们只需要定义一个数组就可以解决了,如:
int score[4][50];
score[0][0] = 80;
score[1][0] = 78;
score[2][0] = 67;
score[3][0] = 88;
...
二维数组的定义
-
类型 数组名[常量表达式][常量表达式]
int a[6][6];
// 6*6,6行6列char b[4][5];
// 4*5, 4行5列double c[6][3];
// 6*3,6行3列 -
在C语言中,内存的存放是线性的。所以不管是一维数组还是二维、三维数组,它们的存放都是线性的。
以二维数组
int b[4][5]
为例首先它在内存中定义一个一维数组出来,进而再在一维数组中每一个元素中再定义一个一维数组出来。也可以说是一维数组的嵌套。
二维数组的访问
与一维数组相似,二维数组同样是以下标来访问
-
**数组名[下标][下标] **
a[0][0];
// 访问a数组中第1行第1列的元素b[1][3];
// 访问b数组中第2行第4列的元素c[3][3];
// 访问c数组中第4行第4列的元素 -
同样需要注意下标的取值范围,以防止数组的越界访问
- 比如
int a[3][4]
,其“行下标”的取值范围是0 ~ 2,“列下标”的取值范围是0 ~ 3,超出任何一个下标的访问都是越界访问。
- 比如
二维数组的初始化
-
由于二维数组在内存中是线性存放的,因此可以将所有的数据写在一个花括号内:
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int main() {int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};int i, j;for (i=0;i<3;i++){for (j=0;j<4;j++){printf("%d", a[i][j]);}printf("\n");}return 0; }
运行结果
1 2 3 4 5 6 7 8 9 10 11 12
-
为了更直观地表示元素的分布,可以用大括号将每一行的元素括起来:
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
或者
int a[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12} };
-
二维数组也可以仅对部分元素赋初值:
int a[3][4] = {{1}, {5}, {9}};
这样会把前三行中每行第一个元素进行赋值,剩余未被赋值元素会用0代替
int main() {int a[3][4] = {{1}, {5}, {9}};int i, j;for (i=0;i<3;i++){for (j=0;j<4;j++){printf("%d ", a[i][j]);}printf("\n");}return 0; }
运行结果
1 0 0 0 5 0 0 0 9 0 0 0
-
如果希望整个二维数组初始化为0,那么直接在大括号里写一个0即可:
int a[3][4] = {0};
int main() {int a[3][4] = {0};int i, j;for (i=0;i<3;i++){for (j=0;j<4;j++){printf("%d ", a[i][j]);}printf("\n");}return 0; }
运行结果:
0 0 0 0 0 0 0 0 0 0 0 0
-
C99同样增加了一种新特性:指定初始化的元素。这样就可以只对数组中某些指定元素进行初始化赋值,而未被赋值的元素自动初始化为0:
int a[3][4] = {[0][0] = 1, [1][1] = 2, [2][2] = 3};
int main() {int a[3][4] = {[0][0] = 1, [1][1] = 2, [2][2] = 3};int i, j;for (i=0;i<3;i++){for (j=0;j<4;j++){printf("%d ", a[i][j]);}printf("\n");}return 0; }
运行结果:
1 0 0 0 0 2 0 0 0 0 3 0
-
二维数组的初始化也能偷懒,让编译器根据元素的数量计算数组的长度。但只有第1维的元素个数可以不写,其他维度必须写上:
int a[][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};
int main() {int a[][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};int i, j;for (i=0;i<3;i++){for (j=0;j<4;j++){printf("%d ", a[i][j]);}printf("\n");}return 0; }
运行结果
1 2 3 4 5 6 7 8 9 10 11 12