【C】LV1-05-C语言-5数组与字符串
摘要:
本文主要是记录C语言
笔记——数组与字符串。
一、数组
数组是构造数据类型之一,是具有一定顺序关系的若干个变量的集合,组成数组的各个变量称为数组的元素,数组中各元素的数据类型要求相同,用数组名和下标确定。数组可以是一维的,也可以是多维的。
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] |
【注意】数组名不可以进行自加自减运算,自然这里的行名就相当于每一行元素的数组名,也不可以进行自加自减运算。
二、字符串与字符数组
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字符数组访问
与一般的数组一样,访问元素时只能一个逐个字符访问,但是若是只输出的话要注意赋值方式的不同,输出时也会不一样。
- 逐个字符访问输出
1 |
|
注意数组b[]
打印是有问题的。
- 以字符串的形式访问输出
1 |
|
注意数组b[]
打印是有问题的。
【总结】
(1)注意字符数组中的\0
,当输出时,该字符会对输出结果造成影响。
(2)注意在字符数组逐个赋值时,若没有字符串结束标志\0
,那么尽量不要使用printf
函数的%s
格式进行输出,就如上图中的b[]
数组
3字符串处理函数
在使用字符串处理函数时,要加上头文件<string.h>
。
1 | /* 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。 */ |