LV01-05-C语言-数组与字符串
本文主要是C语言——数组与字符串相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
参考方向 | 参考原文 |
--- | --- |
一、数组
数组是构造数据类型之一,是具有一定顺序关系的若干个变量的集合,组成数组的各个变量称为数组的元素,数组中各元素的数据类型要求相同,用数组名和下标确定。数组可以是一维的,也可以是多维的。
1.一维数组
1.1一维数组定义
- 概念
一维数组是指只有一个下标的数组。它在计算机的内存中是连续存储的。
- 一维数组声明
1 | storage_type data_type array_name[ array_size ]; |
storage_type | 存储类型(可以说明也可以不说明) |
data_type | 任意有效的 C 数据类型(必须说明) |
array_name | 数组名称(必须说明) |
array_size | 数组长度(有些情况可以省略) |
例如:
1 | float m[12]; /* 定义一个长度为 12 的浮点型数组 */ |
【注意】
(1)数组必须先声明才能进行使用和赋值。
(2) C语言 对数组不作越界检查,使用时要注意。
(3)用变量定义数组维数时,该变量必须提前告知。例如 int i = 6;int a[i];
(4) sizeof(array_name) 可以获取整个一维数组所占据的字节数(等于元素个数x元素类型所占字节数)。
1.2一维数组元素访问
- 访问格式
1 | array_name[index] /* 数组名[元素索引(也可以说是下标)] 索引从0开始 */ |
例如:
1 | int a[10]; |
【注意】
(1)数组必须定义之后才能够访问。
(2)数组的元素只能逐个访问,不能一次性访问整个数组。
(3)下标可以是整型表达式或者常量。
- 遍历一维数组
1 | int a[10]; |
1.3一维数组元素初始化
1 | int a[6] = {0, 1, 2, 3, 4, 5}; /* 在声明数组时直接进行初始化 */ |
【注意】
(1)数组不初始化,其元素值为随机数
(2)对 static 数组元素不赋初值,系统会自动赋以 0 值
(3)只给部分数组元素赋初值,剩余元素自动赋值为 0 。
【说明】
数组声明形式 | 等价写法 |
static int a[3]; | static int a[3]; a[0] = 0; a[1] = 0; a[2] = 0; |
int a[3] = {1, 2}; | int a[3]; a[0] = 1; a[1] = 2; a[2] = 0; |
int a[] = {1, 2, 3} | int a[3]; a[0] = 1; a[1] = 2; a[2] = 3; /* 编译系统根据初值个数确定数组维数 */ |
1.4一维数组的存储空间
数组是一个整体,它的内存是连续的,相邻元素地址相差 data_type 所占字节长度。
例如:
1 |
|
a[3] 在内存中的结构如下图所示, a[3] 数组为 int 类型,在 64 位平台下占 4 个字节,所以相邻元素之间地址相差 4 。
1.5一维数组的数组名
一维数组的数组名代表了该数组的起始地址,可以通过该数名推出各个元素的地址,并通过指针(后边说,先提一下)进行访问。
1 |
|
【注意】数组名不可以进行自加自减运算。
2.二维数组
2.1二维数组声明
1 | 数据类型 数组名[ 常量表达式1 ][ 常量表达式2 ]; |
例如:
1 | int a[2][3]; /* 定义了一个2行3列的二维数组 */ |
表示定义了一个 2行3列的二维数组 ,元素共有 2x3=6 个( 元素个数 = 行数 x 列数 )。
【注意】
(1)声明时列数不能省略,**行数可以省略(定义时进行初始化,编译器可以判别出行数时)**。
(2) sizeof(数组名) 可以获取整个二维数组所占据的字节数(等于元素个数x元素类型所占字节数)。
(3)2) sizeof(行数组名) 可以获取二维数组某行所占据的字节数(等于该行元素个数x元素类型所占字节数)。
2.2二维数组访问
- 访问格式
1 | array_name[row_index][col_index] /* 数组名[行索引][列索引] 行索引和列索引都是从0开始*/ |
storage_type | 存储类型(可以说明也可以不说明) |
data_type | 任意有效的 C 数据类型(必须说明) |
p_name | 指针变量名 |
1 | int a[2][3]; |
- 遍历二维数组
1 | int a[2][3]; |
2.3二维数组元素初始化
1 | int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; /* 分行赋值 */ |
【注意】
(1)与一维数组相同,数组不初始化,其元素值为随机;对 static 数组元素不赋初值,系统会自动赋以 0 值;只给部分数组元素赋初值,剩余元素自动赋值为 0 。
(2)当采用分行赋值的方法声明二维数组时,二维数组的行数可以省略,行数会由编译器自己进行计算。
【说明】
1 | /* 声明形式 1 (两种写法等价) */ |
2.4二维数组的存储空间
二维数组在内存中是一维的,存储时行序优先。
1 |
|
a[2][3] 在内存中的结构如下图所示, a[2][3] 数组为 int 类型,在 64 位平台下占 4 个字节,所以相邻元素之间地址相差 4 。
2.5二维数组的数组名
与一维数组一样,二维数组的数组名也代表了该二维数组的起始地址。二维数组的另一种理解方式:二维数组可以看做是由多个元素组成的一维数组,而每个元素又是一个数组,从而合起来构成了二维数组。
例如:
1 |
|
经过打印地址发现:
(1) a 为整个二维数组的数组名,代表了整个二维数组的起始地址。
(2) a[0], a[1] 也分别代表了第一行和第二行起始数据的地址。
经过分析和验证,可以得到:一个二维数组,按行可以分为多个一维数组,以 int a[2][3] 为例,该 2行3列 的二维数组就可以理解为 2个元素 组成,每个元素都是一个一维数组组成,每个一维数组的数组名就是二维数组名加上第一个下标,即 a[0], a[1] 。
行名(代表了地址) | 每行元素 | ||||
a | a[0] | &a[0][0] | a[0][0] | a[0][1] | a[0][2] |
a + 1 | a[1] | &a[1][0] | a[1][0] | a[1][1] | a[1][2] |
【注意】数组名不可以进行自加自减运算,自然这里的行名就相当于每一行元素的数组名,也不可以进行自加自减运算。
3.数组元素个数
我们有的时候需要程序去自动计算数组的长度,有下边两种方式:
1 | len = sizeof(数组名)/sizeof(数组类型); |
已知数组元素类型的时候,我们可以使用第一种方式,当未知数组元素类型的时候,我们可以使用后边这种方式,直接使用数组中首个元素作为每个元素大小计算的标准。
二、字符串与字符数组
1.字符串
1.1字符串定义
字符串是一系列连续的字符的组合,要想在内存中定位一个字符串,除了要知道它的开头,还要知道它的结尾。找到字符串的开头很容易,知道它的名字(字符数组名或者字符串名)就可以,但是结尾怎么办呢?在 C语言 中,字符串总是以 ‘\0’ 作为结尾,所以 ‘\0’ 也被称为字符串结束标志,或者字符串结束符。由 “ “ 包围的字符串会自动在末尾添加 ‘\0’ 。例如, “abc123” 从表面看起来只包含了 6 个字符,其实不然, C语言 会在最后隐式地添加一个 ‘\0’ 。
‘\0’ 是 ASCII 码 表中的第 0 个字符,英文称为 NULL ,中文称为“空字符”。该字符既不能显示,也没有控制功能,输出该字符不会有任何效果,它在 C语言 中唯一的作用就是作为字符串结束标志。
1.2字符串长度
所谓字符串长度,就是字符串包含了多少个字符(**不包括最后的结束符 ‘\0’ **)。例如: abc123 字符串在内存中占据的空间是 6 + 1 = 7 个字符的空间,但是这个字符串的长度为 6 。
2.字符数组
2.1字符数组定义
元素的数据类型为字符类型的数组称为字符数组,字符数组实际上是一系列字符的集合,例如:
1 | char a[10]; /* 定义一个长度为10的一维字符数组 */ |
2.2字符数组初始化
- 一维字符数组初始化
1 | /* 一维字符数组初始化 */ |
【注意】
(1)给字符数组赋值时,通常将字符串一次性地赋值(可以指明数组长度,也可以不指明),而不是一个字符一个字符地赋值。
(2)字符数组只有在定义时才能将整个字符串一次性地赋值给它,一旦定义完了,就只能一个字符一个字符地赋值了。
(3)字符数组在未赋初值时,内容也是随机的。
(4)当使用字符串给字符数组赋值,且未给出长度时,创建的字符数组真实长度为字符串长度 + 1,原因就在于用字符串进行赋值时,字符串结束处自带一个字符串结束标志: \0 。所以这也就意味着,我们若是要指定字符数组的大小,一定要留够字符串长度 + 1的长度,这样字符串才更加完整,也会比较严谨。
【说明】
字符数组声明形式 | 等价写法 |
char a[3] = {'h', 'e', 'l'}; | char a[3]; a[0] = 'h'; a[1] = 'h'; a[2] = 'l'; |
char a[3] = {'h'}; | char a[3]; a[0] = 'h'; a[1] = '\0'; a[2] = '\0'; |
char a[3] = {"hel"}; | char a[3]; a[0] = 'h'; a[1] = 'e'; a[2] = 'l'; /* 这里若是长度定义为4就会更加合理 */ |
char a[] = {"hel"}; | char a[4]; a[0] = 'h'; a[1] = 'e'; a[2] = 'l';a[3] = '\0'; |
- 二维字符数组初始化
1 | char fruit[][7]={"Apple", "Orange"}; |
效果如下表
行名 | 每行元素 | ||||||
fruit[0] | 'A' | 'p' | 'p' | 'l' | 'e' | '\0' | '\0' |
fruit[1] | 'O' | 'r' | 'a' | 'r' | 'g' | 'e' | '\0' |
2.3字符数组访问
与一般的数组一样,访问元素时只能一个逐个字符访问,但是若是只输出的话要注意赋值方式的不同,输出时也会不一样。
2.3.1逐个字符访问输出
1 |
|
注意数组 b[] 打印是有问题的。
2.3.2以字符串的形式访问输出
1 |
|
注意数组 b[] 打印是有问题的。
【总结】
(1)注意字符数组中的 \0 ,当输出时,该字符会对输出结果造成影响。
(2)注意在字符数组逐个赋值时,若没有字符串结束标志 \0 ,那么尽量不要使用 printf 函数的 %s 格式进行输出,就如上图中的 b[] 数组
3字符串处理函数
在使用字符串处理函数时,要加上头文件 <string.h> 。
1 | /* 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。 */ |