一、一维数组的创建和初始化
1、数组的创建
数组是一组相同类型元素的集合。
数组的创建方式:
type_t arr_name [const_n]; //type_t 是指数组的元素类型 //const_n 是一个常量表达式,用来指定数组的大小
首先我们就来看看数组如何创建~~
int a1[5]; char a2[6]; float a3[7]; double a4[4 + 4]; //也可以是一个表达式
- 虽然指定数组大小可以是一个常量,但VS不支持是一个变量
int n = 0; scanf("%d", &n); int arr[n];
- 其实这种写法是可以的,因为在C99中引入了
变长数组
的概念 - 变长数组支持数组的大小使用变量来指定。
- 但是变长数组不是数组的长度可以变化,而是数组的大小可以用变量来指定
2、数组的初始化
- 首先要来辨析一下初始化和赋值的区别。千万不可以混淆
int n = 0; //初始化 int m; m = 0; //赋值
- 接下去就来看看数组的初始化
- 首先是整型数组
//1.不完全初始化,数组个数10个。第一个元素为1,其余9个位0 int arr1[10] = { 1 }; //2.完全初始化,数组个数10个 int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 }; //3.若没有指定数组的个数,则初始化了几个这个数组的大小就为几 int arr3[] = { 1,2,3,4,5 };
- 然后是字符数组
//1.数组大小为4,初始化四位,abc + '\0' char ch1[] = "abc"; //2.数组大小为3,初始化前三位,abc char ch2[] = { 'a', 'b', 'c' };
- 如果以字符串的形式进行初始化,则默认在最后加上一个
\0
; - 若是以单个字符的形式初始化,则数组大小即为初始化的字符个数
- 其中在数组未完全初始化中,后面默认是
0
,在ASCLL码中0就是\0
- 我们打印一下看一下~~
- 注意: 当变量在
全局范围
默认初始化为0,当变量在局部范围
内默认没有初始化,放的是随机值
3、一维数组的使用
对于数组的使用,我们有一个操作符 []
,下标引用操作符,它其实就数组访问的操作符
- 我们来通过这个操作符来访问数组中的内容
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
- 上面这种对于循环中要访问的数组个数已经写死了,如果修改一下数组的元素个数,那循环的结束条件就也要修改,此时我们就可以考虑使用到
sizeof()
去首先计算出数组的大小~~
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
注意: 在数组创建的时候不能使用变量,而在使用的时候就可以~~
小结:
- 数组是使用下标来访问的,下标是从0开始
- 数组的大小可以通过计算得到
4、 一维数组在内存中的存储
- 要想知道数组是如何在内存中存放的,那就要将每一个元素的地址打印出来观察一下
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < sz; i++) { printf("&arr[%d] = %p \n", i, &arr[i]); } return 0; }
- 可以看到,对于每一个数组元素之间,在内存中都是差了4个字节,因为整型是4个字节
小结:
- 一维数组在内存中是连续存放的
- 随着数组下标的增长,地址是由低到高变化的
二、二维数组的创建和初始化
1、二维数组的创建
- 首先那来看一下各种数据类型的二维数组创建
int main() { int arr1[3][4]; //整型二维数组 double arr2[3][5]; //字符型二维数组 float arr3[4][5]; //浮点型二维数组 return 0; }
2、二维数组的初始化
int main() { int arr1[3][4] = { 1,2,3,4 }; int arr2[3][4] = { {1,2},{4,5} }; int arr3[][4] = { {2,3},{4,5} }; return 0; }
- 创建完后,那还要对其对其进行初始化。
- 可以看到我默认初始化了五个元素之后,因为这个二维数组的是三行四列的,所以第五个元素自动归位第二行的第一个元素
- 我们还可以指定初始化每一行的元素,一行表示一个大括号,只需要在大括号里为每一行也加上花括号,然后在括号里写上这一行要初始化的数据
对于二维数组在初始化的时候可以省略行,但是不可以省略列
那么为什么不能省略列呢?
在C语言中,数组的大小在声明时需要指定,包括行和列。这是因为C语言中的数组是一块连续的内存空间,编译器在编译时需要知道数组的大小以便正确分配内存。
如果省略列,编译器将无法确定每个元素在内存中的偏移量,因此无法正确计算数组的地址。指定列数有助于编译器进行正确的地址计算和内存分配
3、二维数组的使用
初始化好了,我们可以将一个二维数组打印在屏幕上
int arr[][4] = { {1,2},{3,4},{5} }; for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); }
4、二维数组在内存中的存储
那么数组再内存中是怎么存储的呢?
在这里插入图片描述 4、二维数组在内存中的存储 那么数组再内存中是怎么存储的呢? int arr[][4] = { {1,2},{3,4},{5} };
- 对于二维数组来说,内存中是连续存放的
数组首元素地址:
- 对于一个数组的数组名来说就是这个数组的首元素地址,我们可以根据一个数组的首元素地址来访问到这个数组,然后就可以访问到这个数组中的所有内容
- 对于这个二维数组来说,因为它一行就是一个一维数组,因此我们就可以说
arr[1]
是第一行的首元素地址arr[2]
是第二行的首元素地址arr[3]
是第三行的首元素地址
- 然后便可以根据每一行的首元素地址的偏移量访问到这行的所有内容
三、数组越界
边界值考虑不当导致越界访问
int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 5; ++j) { printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
- 对于每一行的内部访问,从
0~4
会依次访问五个元素,但是每一行只有四个元素 - 当本行访问完后就会去访问下一行的第一个元素。然后第二行又从下标为0的位置开始访问,到了最后一行的时候,没有再下一行可以访问了,那第五个访问到的也就是一个随机值
- 可以通过图示看一下在内存中是如何进行访问的
数组大小不足以承载输入的字符数
char arr[] = ""; scanf("%s", arr); printf("%s\n", arr);
- 上面题目中
arr[]
并没有指定数组的大小,因此数组大小由初始化的字符个数决定。 - 可以看到这里只初始化了一个空字符,也就相当于只有一个
\0
,那么这个数组的大小即为1。所以当我scanf
输入一个长度大于1的字符串时,其实就会造成数组越界的问题【arr数组周围的堆栈被破坏即为数组越界】
int arr1[] = { 0 }; for (int i = 0; i < 10; ++i) { arr1[i] = i; }
- 这个整型数组的大小为初始化内容的大小,但是下面的操作访问了10个字节,会越界访问~~
C语言之详解数组【附三子棋和扫雷游戏实战】(二):https://developer.aliyun.com/article/1426998