C语言学习四:数组
数组入门
概念
-
逻辑: 一次性定义多个相同类型的变量,并且给他分配一片连续的内存
-
语法:
int arr [5];
-
初始化
只有在定义的时候赋值,才可以称为初始化。数组只有在初始化的时候才可以统一赋值。
1
2
3
4
5
6
7
8
9int arr [5] = {1,2,3,4,5} ; // 正确,定义并初始化数组
int arr [5] = {1,2,3} ; //可以, 为不完全初始化,后面的两个为0;若完全的不初始化则为随机值
int arr [5] = {1,2,3,4,5,6,7,8,9} ; // 错误(但是可以用),越界初始化, 越界部分将会被编译器舍弃
int arr [ ] = {1,2,3,4,5,6,7,8,9} ; // 可以, 用户没有直接给定数组大小,
// 但是有初始化, 因此数组的大小会在初始化时确定, 大小为 9
int arr [ ]; // 错误, 没有给定大小也没有初始化, 因此数组的内存大小无法确定,系统无法分配
注意:
数组在定义的时候必须确定他的大小。
说白了就是中括号中[]必须有数组的大小,如果没有就必须初始化数组元素引用:
- 存储模式:一片连续的内存,按照数据的类型进行分割成若干个大小相同的格子
- 元素的下标与偏移量:以数组开头为基础的偏移的量(数据类型大小)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16int arr [5] = {1,2,3,4,5} ; // 定义并初始化数组
printf("arr[0]:%d\n" , arr[0]);
arr[0] = 99 ; // 把数组的第1个元素( 偏移量为0 )修改为 99
printf("arr[0]:%d\n" , arr[0]);
arr[5] = 250 ; //"错误" 越界访问, 很有可能造成非法访问
printf("arr[5]:%d\n" , arr[5]);
arr = {9,8,7,6,5,4}; // 整体赋值只允许在初始化中
arr = 100 ; // 错误
printf("sizeof(arr):%ld\n" , sizeof(arr));
int len = sizeof(arr) / sizeof(int) ; // 求数组元素的个数
for (size_t i = 0; i < len ; i++)
{
printf("arr[%ld]:%d\n" , i , arr[i]);
}
字符数组
-
概念: 专门用来存放字符类型数据的数组,称为字符数组
-
初始化+引用:
1
2
3
4
5
6
7
8
9
10char ch1 [5] = {'H','e','l','l','o'} ; // 定义一个字符类型的数组并把'H','e','l','l','o' 一个一个存进去
char ch2 [5] = {"Hello"} ; // 定义一个字符型的数组, 并把 "Hello" 字符串存放到数组中 ,因此该数组为字符串数组(后面会有一个空字符`\0`),ch2[0]为'H',ch2[5]为空字符'\0',在终端中看不到
char ch3 [5] = "Hello" ; // 与ch2 一样, 大括号可以省略
ch3[1] = 'E' ; // 可以, 把数组中第二个元素‘e’修改为‘E’
ch3 = "hi" ; // 不可以, 只有在初始化的时候才能整体赋值
printf("%s\n" , ch1); // 在访问ch1的时候并没有发现结束符,因此很有可能会把后面ch2的内容一并输出注意:ch1 为字符数组,它没有结束符,因此在打印输出的时候应该避免使用 %s 进行输出,有可能会造成越界访问。
思考
-
定义数组时没有初始化会怎么样?
没有初始化的情况下内存的内容是随机值(内存中原有的内容)

-
不完全初始化又会怎么样?
不完全初始化,已经初始化部分是已知值,其它则是0
-
补充
注意:
1
2char buf[5]={1,2,3,4,5}; //把将1,2,3,4,5当作字符的ASCII值存放在数组中
char buf[5]={'1','2','3','4','5'} //存放在内存中的是字符1,2,3,4,5所对应的ASCII值
点击此处展开/收起内容,来自 chatgpt 的解释
-
char buf[5]={1,2,3,4,5};
表示将整数值 1、2、3、4 和 5 直接存储到
buf数组中。在 C 语言中,
char类型是一种整数类型,可以存储整数值。因此,整数值 1、2、3、4 和 5 会被直接存储到buf数组的连续内存位置中。需要注意的是,这些整数值并不是字符的 ASCII 码值。当您使用整数值初始化
char数组时,编译器会将这些整数值直接存储到数组的内存位置中,而不会将它们解释为字符。
如果您希望将整数值解释为对应的字符,可以使用类型转换或字符操作函数来实现。例如,可以使用printf函数将buf数组中的整数值作为字符进行打印:for (int i = 0; i < 5; i++) { printf("%c ", buf[i]); } -
char buf[5]={‘1’,‘2’,‘3’,‘4’,‘5’}
表示将字符 ‘1’、‘2’、‘3’、‘4’ 和 ‘5’ 的 ASCII 码值存储到
buf数组中。具体地说,字符 ‘1’ 的 ASCII 码值是 49,字符 ‘2’ 的 ASCII 码值是 50,字符 ‘3’ 的 ASCII 码值是 51,字符 ‘4’ 的 ASCII 码值是 52,字符 ‘5’ 的 ASCII 码值是 53。这些 ASCII 码值会被存储到
buf数组的连续内存位置中。
数组进阶
数组名的含义
数组名在不同的场合下的含义不同:
-
表示整个数组的首地址:
- 在数组定义的时候
- 在使用 sizeof 运算符中数组名表示整个数组的首地址(求得整个数组的大小)
- 在取地址符 中 &arr , arr 表示整个数组
-
表示整个数组首元素的首地址:
- 其它情况
1 | int arr[10] ; // arr 表示整个数组的首地址 |
数组下标
数组的下标实际上只是编译器提供一种简写,实际上如下:
1 | int a [100] ; |
通过加法交换律,有如下结果:
1 | a[10] = 250 ; |
字符串常量
字符串常量是一个被存放在常量区的字符串,实际上也可称为一个匿名数组。
匿名数组,同样满足数组名的含义。
1 | char * msg1 = "Hello" ;// "Hello" 字符串常量首元素的首地址 |
字符串在内存种的存储方式:

零长数组
概念: 数组的长度为0 , char arr [0] ;
用途:一般会放在结构体的末尾, 作为可变长度的入口。(数组是唯一一个允许越界访问的载体)
1 | struct node |

变长数组
概念: 使用一个变量作为数组的长度(元素的个数)。
重点: 变长数组并不是说在任意时候他的长度可以随意变化,实际上只是在定义之前数组的长度是未知的由一个变量来决定,但是定义语句过后变长数组的长度由定义那一刻变量的大小来决定。
1 | int a = 200 ; // a 作为一个普通的变量 , 200 则可以作为arr 的长度 |
注意:
- 因为数组的长度未确定,因此它不允许初始化。
- 在使用的时候可以通过该变长数组来有限的节省内存空间。
多维数组
-
概念:数组内部的成员也是数组
int a [2][3] ;
-
定义与初始化
1
2int arr[2][3] = { {1,2,3} , { 4,5,6} };
int arr1[2][3] = { 1,2,3,4,5,6}; -
如何引用
1
2arr[0] [0] = 100 ; // 数组:(通过下标来访问) //arr[0]指{1,2,3}
*(*(arr+0)+0) = 100 ; // 通过指针偏移来访问 -
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int arr1[2][3] = {1, 2, 3, 4, 5, 6};
int *p = arr; // p指向数组arr 的首元素,即{1, 2, 3}的地址
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
printf("arr[%d][%d]:%d\t", i, j, arr[i][j]);
}
}
printf("\n");
for (int i = 0; i < 6; i++)
{ // *arr 得到元素{1, 2, 3}中的 1 的地址, + 1 则是加一个 int 类型
printf("*(*(arr+%d)):%d\t", i, *(*(arr) + i));
}
printf("\n");
for (int i = 0; i < 6; i++)
{ // arr 指的是首元素的首地址 {1,2,3} 的首地址 + 1则 + 3个整型
printf("*(*(arr+%d)):%d\t", i, *(*(arr + i)));
}
for (int i = 0; i < 6; i++)
{ // p 只是一个普通的整型指针, 与二维数组没有任何的关系
printf("*(p+%d):%d\n" , i ,*(p+i));
}





