知识点#
6.1 一维数组#
- @ 一维数组图
- ![[白板/P12 一维数组.canvas|P12 一维数组]]
6.1.1 一维数组的一般形式#
[!tip] 一维数组定义
- @ 一维数组: 有序数据的集合,包含若干个同类型元素,通过数组名加下标引用
- 一般形式:
类型说明 数组名[常量表达式];
int a[10]; // 定义数组a,有10个元素
定义要点#
- ! 数组名后面是中括号
[],不能写成圆括号() - !
[10]中的10表示有10个元素,下标从0开始,即a[0]~a[9],不包括a[10] - ! 非法引用
a[10]时系统不提示错误,但会产生巨大程序隐患(覆盖其他变量内存)
int a[10];
a[10] = 8; // 非法!超出下标范围,系统不报错但会留下巨大隐患
- ! 常量表达式不能包含变量,C 语言不允许对数组大小做动态定义
int i = 8;
int a[i]; // 非法!变量不能用于定义数组大小
6.1.2 一维数组元素的引用#
[!tip] 数组元素引用
- 只能引用数组中的元素,不能引用整个数组
- 引用形式:
数组名[下标]
int i, a[10];
for(i = 0; i < 10; i++) // 给数组赋值
a[i] = i;
for(i = 9; i >= 0; i--) // 逆序输出
printf("a[%d]=%d\n", i, a[i]);
6.1.3 一维数组的初始化#
| 初始化方式 | 示例 | 说明 |
|---|---|---|
| 不给初值 | int a[10]; | 元素值为随机值 |
| 全部赋初值 | int a[10] = {9,8,10,20,9,8,7,6,5,4}; | 每个元素都有值 |
| 部分赋初值 | int a[10] = {9,8,10,20}; | 未赋值的元素自动为 0 |
| 省略数组长度 | int a[] = {1,2,3,4,5}; | 等价于 int a[5] |
- ! 如果只给部分元素赋初值,其余元素自动赋为
0 - ! 只有在全部赋初值时才能省略数组长度
- ! 若数组长度与初值个数不同,长度数字不能省略
int a[10] = {1,2,3,4,5}; // 只初始化了5个元素,后5个为0
6.2 二维数组#
- @ 二维数组图
- ![[白板/P13 二维数组.canvas|P13 二维数组]]
6.2.1 二维数组的一般形式#
[!tip] 二维数组定义
- 一般形式:
类型说明 数组名[常量表达式][常量表达式];- 理解方式: 按行和列理解,也可以看成"元素为一维数组"的一维数组
float a[3][4]; // 3行4列的数组,共12个元素
// 不能写成 a[3,4],这是错误的写法
二维数组的理解#
a[3][4]可以把a看成一维数组,有3个元素a[0]、a[1]、a[2]- 每个元素又是一个包含4个元素的一维数组
元素引用范围#
- ! 第一维下标范围:
02,第二维下标范围:03 - !
a[3][4]在定义时表示维数,在引用时表示下标值,完全是两个意思
内存存放顺序#
- ! 二维数组在内存中按行存放:先存第一行,再存第二行
- ! 多维数组:第一维下标变化最慢,最右边维度的下标变化最快
6.2.2 二维数组元素的引用#
int a[3][4];
int i, j;
// 用循环嵌套给二维数组赋值
for(i = 0; i < 3; i++)
for(j = 0; j < 4; j++)
a[i][j] = i * j;
// 用循环嵌套输出
for(i = 0; i < 3; i++)
for(j = 0; j < 4; j++)
printf("a[%d][%d]=%d\n", i, j, a[i][j]);
6.2.3 二维数组的初始化#
| 初始化方式 | 示例 | 说明 |
|---|---|---|
| 按行赋值 | 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},{3,4}}; | 未赋值的为 0 |
| 省略第一维长度 | int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; | 第二维不能省 |
- ! 全部赋初值时可省略第一维长度,但第二维长度不能省略
- ! 部分赋初值时也可省略第一维长度,但应分行赋初值
int a[][4] = {{0,0,3},{0},{0,10}};
6.3 字符数组#
- @ 字符数组图
- ![[白板/P14 字符数组.canvas|P14 字符数组]]
6.3.1 字符数组的定义#
[!tip] 字符数组
- @ 字符数组: 用来存放字符数据的数组,一个元素存放一个字符
- 定义方式与整型数组类似,类型为
char
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';
6.3.2 字符数组的初始化#
| 初始化方式 | 示例 | 说明 |
|---|---|---|
| 逐个字符赋值 | char c[10] = {'I',' ','a','m',' ','h','a','p','p','y'}; | 好理解 |
| 省略数组长度 | char c[] = {'I',' ','a','m',' ','h','a','p','p','y'}; | 等价于 char c[10] |
| 初值个数大于数组长度 | char c[8] = {'I',...共10个...}; | 编译报错 |
| 初值个数小于数组长度 | char c[12] = {'I',' ','a','m',' ','h','a','p','p','y'}; | 多余元素可能为 '\0' |
6.3.3 字符串和字符串结束标记#
[!tip] 字符串结束标记
- @
'\0': 字符串结束标志,ASCII 码为0,用来标记字符串的结束- ! 重点中的重点,面试常考
用字符串常量初始化字符数组#
// 逐个字符初始化:数组长度为10
char c[] = {'I',' ','a','m',' ','h','a','p','p','y'};
// 用字符串常量初始化:数组长度为11!系统自动在末尾加 '\0'
char c[] = "I am happy"; // 等价于 char c[11]
- ! 用字符串常量初始化时,系统自动在末尾增加
'\0',所以数组长度比可见字符数多1 - ! 用逐个字符初始化时,系统不会自动加
'\0'
两种初始化的区别#
char c[] = {'I',' ','a','m',' ','h','a','p','p','y'}; // 长度10,没有'\0'
char c[] = "I am happy"; // 长度11,有'\0'
// 这两种写法不等价!
// 但下面这种写法等价:
char c[] = {'I',' ','a','m',' ','h','a','p','p','y','\0'}; // 长度11
char c[] = "I am happy"; // 长度11
字符数组大小必须够放 '\0'#
char c[10] = "I am happy"; // 错误!需要11个位置(10个字符+'\0')
char c[11] = "I am happy"; // 正确,刚好能放下
char c[100] = "I am happy"; // 正确,更可以
- ! 没有
'\0'会导致printf输出时出现垃圾信息(一直输出直到偶然遇到'\0') - ! 建议使用字符串常量方式初始化:
char c[100] = "I am happy";
6.3.4 字符数组的输入/输出#
输出字符串#
- 用
%s格式符输出整个字符串 printf遇到'\0'就停止输出- ! 输出的字符串中不包含
'\0' - ! 数组长度大于字符串实际长度时,也只输出到
'\0'结束 - ! 包含多个
'\0'时,遇到第一个'\0'就停止
char c[] = "China";
printf("%s\n", c); // China
输入字符串#
- 用
scanf和%s格式符输入 - ! 输入的字符串长度必须短于数组长度,要给
'\0'留位置 - !
scanf遇到空格就认为字符串结束
char c[6];
scanf("%s", c); // 最多输入5个字符,给'\0'留位置
printf("%s\n", c);
- !
scanf中字符数组名前不需要加&,因为数组名代表首地址
// 输入多个字符串,以空格分隔
char str1[10], str2[10], str3[10];
scanf("%s %s %s", str1, str2, str3);
// 输入: how are you
// str1="how", str2="are", str3="you"
// 但如果只用一个变量接收
char str[100];
scanf("%s", str);
// 输入: how are you
// str只得到"how",后面的被舍弃
6.3.5 字符串处理函数#
[!tip] 字符串处理函数
- 使用这些函数需要包含头文件
#include <string.h>
puts(字符数组) — 输出字符串#
char str[100] = "Are you ok?";
puts(str); // Are you ok?
// 等价于 printf("%s\n", str);
strcat(字符数组1, 字符数组2) — 连接字符串#
char str1[10] = "one";
char str2[15] = "two";
strcat(str1, str2);
printf("%s\n", str1); // onetwo
- ! 字符数组1必须足够大,能容纳连接后的新字符串
- ! 连接时将字符串1末尾的
'\0'删除,连接字符串2的内容 - ! 连接后只在新字符串末尾保留一个
'\0' - ! 连接后 str2 内容不发生变化
strcpy(字符数组1, 字符串2) — 复制字符串#
char str1[10] = "one1234";
char str2[15] = "two";
strcpy(str1, str2);
// str1 变为 "two"
- ! 字符数组1必须足够大,能容纳被复制的字符串(包括
'\0') - ! 字符数组1必须是数组名,字符串2可以是数组名或字符串常量
- ! 复制时连同
'\0'一起复制 - ! 不能用赋值语句将字符串直接赋给字符数组
str1 = "China"; // 非法!
str1 = str2; // 非法!
strcpy(str1, "China"); // 正确
- ! 区分"定义时初始化"和"赋值"
char str1[10] = "one1234"; // 定义时初始化,合法
str1 = "one1234"; // 赋值,非法!
strcmp(字符串1, 字符串2) — 比较字符串#
| 比较结果 | 返回值 |
|---|---|
| 字符串1 == 字符串2 | 0 |
| 字符串1 > 字符串2 | 正整数 |
| 字符串1 < 字符串2 | 负整数 |
- ! 比较规则:自左至右逐个字符按 ASCII 码值比较,直到出现不同字符或遇到
'\0' - ! 不能用
==比较两个字符串,那比较的是地址
// 错误写法!比较的是地址
if(str1 == str2)
// 正确写法:比较字符串内容
if(strcmp(str1, str2) == 0)
strlen(字符数组) — 获取字符串长度#
char str1[120] = "opel";
int len = strlen(str1); // 4,不包括末尾的'\0'
- ! 返回字符串的实际长度,不包括
'\0' - ! 面试常考:
strlen与sizeof的区别
| 函数/运算符 | strlen(str) | sizeof(str) |
|---|---|---|
| 计算内容 | 字符串实际长度 | 变量所占内存大小 |
是否包含 '\0' | 不包含 | 包含 |
| 与内容关系 | 有关 | 无关 |
| 与定义大小关系 | 无关 | 有关 |
char str1[120] = "opel";
int stlen = strlen(str1); // 4,与str1中内容有关
int sostr = sizeof(str1); // 120,与str1定义时的大小有关

