数组

一维数组

数组的创建

数组是在内存中连续存储的具有相同类型的一组数据的集合

数组创建如下:

1
2
3
4
type arrayName [arraySize];
// type 数组中元素类型
// arrayName 数组名
// arraySize 数组的大小(数组可容纳元素最大个数)。必须是一个大于零的整数常量(常量表达式)

注意:数组大小为 常量表达式,不可为变量。

(其实数组大小可以是变量(可变长数组),C99标准支持,不重要

实例

1
2
3
4
5
6
// 代码1
int arr[10]; // arr可以容纳 10 个类型为 int 的数字。

// 代码2
int count = 10;
int arr2[count]; // error,数组大小不可为变量。

数组初始化

数组的初始化:在创建数组的同时给数组的内容一些合理初始值(初始化)。

数组元素的值由{ }包围,各个值之间以,分隔。

1)给 部分 元素赋值。

{ }中值的个数少于元素个数时,只给前面部分元素赋值,后面的元素自动初始化为 0。

  • 对于short、int、long,就是整数0;
  • 对于char,就是字符 ‘\0’;
  • 对于float、double,就是小数0.0。

只给部分元素赋初值,当{ }中值的个数少于元素个数时,只给前面部分元素赋值,后面的元素默认为0值

赋值的元素少于数组总体元素的时候,剩余的元素自动初始化为 0

1
2
3
4
5
6
7
8
// 大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。
int arr[10] = { 1,2,3,4,5 };
等价:
int arr[10] = { 1,2,3,4,5,0,0,0,0,0 };

int a[10]={0};
等价:
int a[10]={0, 0, 0 , 0, 0, 0, 0, 0, 0, 0};

2)给 全部 元素赋值时,在定义数组时可以不给出数组长度

1
2
3
4
5
// 如果省略掉了数组的大小,数组的大小则为初始化时元素的个数。
int arr[] = { 1,2,3,4,5 };

//如果希望数组中元素全部为1
int a[10]={1, 1, 1 , 1, 1, 1, 1, 1, 1, 1};

3)给全部元素赋值,那么在数组定义时可以不给出数组的长度

注意:数组在创建的时候如果想 不指定数组的确定的大小就得初始化

1
2
3
int a[]={0,2,4,6,8};
等价:
int a[5]={0,2,4,6,8};

注意:

1
2
3
4
5
// 区分以下代码
char arr1[] = "abc";
char arr2[3] = { 'a','b','c' };
printf("%s\n", arr1); //abc
printf("%s\n", arr2); //abc烫烫烫烫蘟bc

一维数组的使用

访问数组元素:使用 [] ,下标引用操作符。

1
2
数组名[下标]
这里的下标与定义数组的形式相同,但含义不同。定义数组是指定的是大小;而这里的下标指的是数组元素的编号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main()
{
int arr[10];
int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组元素个数
int i = 0;
for (i = 0; i < sz; i++)
{
arr[i] = i; // 对数组内容赋值,数组是使用下标来访问的,下标从0开始。
}
for (i = 0; i < sz; i++)
{
printf("Element[%d] = %d\n", i, arr[i]);
}

return 0;
}
1
2
3
// 计算数组大小
int arr[10];
int sz = sizeof(arr) / sizeof(arr[0]);

注意:数组可以使用下标访问,数组的下标是 从 0 开始

1
2
3
4
5
6
int a[5] = { 1,2,3,4,5};   // 这里的5是arrsize,数组大小
a[5] = 6; // error 这里越界了,这个数组的范围是 0-4,最大小标为4
// 下标范围:0 ~ sz-1

int a[5]; // 这里的5是arrsize,数组大小
a[5] = {1, 2, 3, 4, 5}; // 错误1:越界 错误2:给单个元素赋值,不可以加大括号

数组越界

数组的下标是有范围限制的。

数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。

C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就 是正确的。

以下程序错误写法:

1
2
3
4
5
6
7
8
9
10
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
for(i=0; i<=10; i++)
{
printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
}
return 0;
}

一维数组在内存中的存储

数组在内存中是连续存放的。随着数组下标的增长,元素的地址在有规律的递增。

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main()
{
int arr[10] = {0};
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);

for(i=0; i<sz; ++i)
{
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
//输出:
//&arr[0] = 0096FE6C
//&arr[1] = 0096FE70
//&arr[2] = 0096FE74
//&arr[3] = 0096FE78
//&arr[4] = 0096FE7C
//&arr[5] = 0096FE80
//&arr[6] = 0096FE84
//&arr[7] = 0096FE88
//&arr[8] = 0096FE8C
//&arr[9] = 0096FE90

二维数组

二维数组可以看成是由多个长度相同的一维数组构成的。

image-20230218160734853

二维数组的创建

二维数组创建如下:

1
2
3
4
5
type arrayName [length1][length2];
// type 数组中元素类型
// arrayName 数组名
// length1 第一维下标的长度(行数)
// length2 第二维下标的长度(列数)

实例

1
2
int arr[3][4];
char arr[5][5];

二维数组的初始化

1)二维数组的初始化可以按行分段赋值,也可按行连续赋值。

1
2
3
int arr[3][4] = {1,2,3,4};
等价:
int arr[3][4] = {{1,2},{4,5}};

2)二维数组初始化,行可以省略,列不能省略

1
2
3
int arr[][4] = {{2,3},{4,5}};
等价:
int arr[2][4] = {{2,3},{4,5}};

3)可以只对部分元素赋值,未赋值的元素自动取0

1
2
3
4
5
6
7
8
9
int a[3][3] = {{1}, {2}, {3}};
1 0 0
2 0 0
3 0 0

int a[3][3] = {{0,1}, {0,0,2}, {3}};
0 1 0
0 0 2
3 0 0

二维数组的使用

二维数组的使用也是通过下标的方式。下标引用 都是从 0 开始

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
33
34
35
36
int main()
{
int arr[3][4] = { 0 };
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
arr[i][j] = i * 4 + j;
}
}

for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
printf("arr[%d][%d} = %d\n", i, j, arr[i][j]);
}
}

return 0;
}

输出:
//arr[0][0} = 0
//arr[0][1} = 1
//arr[0][2} = 2
//arr[0][3} = 3
//arr[1][0} = 4
//arr[1][1} = 5
//arr[1][2} = 6
//arr[1][3} = 7
//arr[2][0} = 8
//arr[2][1} = 9
//arr[2][2} = 10
//arr[2][3} = 11

二维数组在内存中的存储

image-20230218160752624

二维数组在概念上是二维的,但在内存中是 连续存放 的。

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
int main()
{
int arr[3][4] = { 0 };
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
printf("arr[%d][%d} = %p\n", i, j, &arr[i][j]);
}
}

return 0;
}

输出:
//arr[0][0} = 006FFE30
//arr[0][1} = 006FFE34
//arr[0][2} = 006FFE38
//arr[0][3} = 006FFE3C
//arr[1][0} = 006FFE40
//arr[1][1} = 006FFE44
//arr[1][2} = 006FFE48
//arr[1][3} = 006FFE4C
//arr[2][0} = 006FFE50
//arr[2][1} = 006FFE54
//arr[2][2} = 006FFE58
//arr[2][3} = 006FFE5C

数组名是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
int arr[10] = { 1,2,3,4,5 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%d\n", *arr);

return 0;
}

输出:
//007AFC24
//007AFC24
//1

重要【★★★★★】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
int arr[10] = { 1,2,3,4,5 };
printf("arr = %p\n", arr); // arr表示数组 首元素地址
printf("&arr[0] = %p\n", &arr[0]); // 取出数组 首元素地址
printf("&arr = %p\n", &arr); // 这里取出的是 整个数组的地址,虽然结果一样,但是意义不同

return 0;
}

输出:
//arr = 0118FB90
//&arr[0] = 0118FB90
//&arr = 0118FB90



看到这里,有人就问这不一样的吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main()
{
int arr[10] = { 1,2,3,4,5 };
printf("arr = %p\n", arr); // arr表示数组首元素地址
printf("arr+1 = %p\n", arr+1);
printf("&arr[0]+1 = % p\n", &arr[0]+1);

printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);

return 0;
}
输出:
//arr = 004FF728
//arr + 1 = 004FF72C
//& arr[0] = 004FF728
//& arr[0] + 1 = 004FF72C
//& arr = 004FF728
//& arr + 1 = 004FF750
分析:
第一组:在原本地址上加了 4,增加了一个元素的大小
第二组:在原本地址上加了 4,增加了一个元素的大小
第三组:在原本地址上加了 40,也就是增加了整个数组的大小

总结:

数组名是数组首元素地址。但是有 两个例外(除下面两个例外以外,数组名都表示数组首元素地址)

  1. sizeof(数组名)计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组
  2. &数组名取出的是整个数组的地址。&数组名,数组名表示整个数组

字符数组

注意:

字符数组可以在定义的时候进行初始化赋值,也可以在之后的程序中对其进行赋值操作。例如:

1
2
char str[6] = "hello"; // 在定义时进行初始化赋值
strcpy(str, "world"); // 在程序中对字符数组进行赋值

但是在对字符数组进行赋值时需要注意,使用赋值运算符(=)对字符数组进行赋值会出现错误,需要使用字符串函数如strcpy()等进行操作。例如:

1
2
3
char str[6] = "hello";
str = "world"; // 错误,不能使用赋值运算符进行赋值操作
strcpy(str, "world"); // 正确,使用strcpy()函数进行赋值操作