文章目录
1. 一维数组的创建的初始化
1.1 数组的创建
数组是一组相同类型元素的集合。
数组的创建方式:
type_t arr_name [const_n];
type_t 是指数组的元素类型;
const_n 是一个常量表达式,用来指定数组的大小。
数组的创建:
//代码1
int arr1[10];
//代码2
char arr2[10];
float arr3[1];
double arr4[5];
double arr5[2+3];
//代码3
int n = 10;
int arr6[n];//数组什么时候可以正常创建呢?
注:
- 代码3的数组什么时候可以正常创建呢?
- 在支持C99标准的编译器环境下编译。
- C99中引入了变长数组的概念,变长数组支持数组大小使用变量来指定。变长数组不是数组的长度可以变化,而是数组的大小可以用变量来指定。
- VS2019和VS2022都不支持C99的变长数组,但是很多的OJ 刷题平台是支持的。
1.2 数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
#include <stdio.h>
int main()
{
//数组的初始化
int arr1[10] = {
1 };
//不完全初始化,第一个元素初始化为1,其余元素初始化为0
int arr2[10] = {
1,2,3,4,5,6,7,8,9,10 };
//完全初始化,这里的每一个元素都有被赋值
char arr3[5] = {
'a','b','c' };
//在内存中存储了a,b,c,0,0
char arr4[5] = "abc";
//在内存中存储了a,b,c,\0,0
// \0的ASCII值是0,所以arr4在内存中存储了a,b,c,0,0,但是它实际上和arr3在内存中的存储是不一样的。
int arr5[] = {
1,2,3,4 };
//没有给定元素个数时,数组的元素个数根据初始化的内容来确定,此时元素个数为4
char arr6[] = {
'a','b','c' };
//在内存中这里只存储了a,b,c 三个字符
char arr7[] = "abc";
//在内存中这里存储了a,b,c,\0四个字符,字符串的结束标志是\0,所以\0也要存储的数组中
char arr8[3] = {
'a',98,'c' };
//这里的98是b的ASCII值,所以这里存储了a,b,c三个元素
return 0;
}
我们调试起来看一下:
1.3 一维数组的使用
对于数组的使用我们之前介绍了一个操作符: [] ,下标引用操作符。它其实就数组访问的操作符。
//代码1:
#include <stdio.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
int i = 0;//对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以:int i = 0;做下标
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);//arr[i]表示数组的一个元素,而不是创建数组
}
return 0;
}
//代码2:
#include <stdio.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
int i = 0;
//计算数组的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
总结:
- 数组是使用下标来访问的,下标是从0开始。
- 数组的大小可以通过计算得到。
int arr[10]; int sz = sizeof(arr) / sizeof(arr[0]);
1.4 一维数组在内存中的存储
接下来我们探讨数组在内存中的存储
#include <stdio.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
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;
}
输出结果如下:
这里 %p 打印的是十六进制数,十六进制通常用数字0、1、2、3、4、5、6、7、8、9和字母A、B、C、D、E、F(a、b、c、d、e、f)表示,C表示12,十六进制中的10,在十进制中表示16。
仔细观察输出结果,我们可以看到每个地址相差4,因为这里的数组是int 类型的数组,一个int 类型在内存中占4字节。随着数组下标的增长,元素的地址,也在有规律的递增。
由此可以得出结论:
- 数组在内存中是连续存放的。
- 随着数组下标的增长,地址也是由低到高变化的。
2. 二维数组的创建和初始化
2.1 数组的创建
二维数组是一个几行几列的数组。
#include <stdio.h>
int main()
{
//二维数组的创建
int arr1[3][4];
char arr2[3][5];
double arr3[2][4];
return 0;
}
2.2 数组的初始化
#include <stdio.h>
int main()
{
//二维数组初始化
int arr1[3][4] = {
1,2,3,4,5 };
//逐个初始化,按顺序初始化,前面初始化为1,2,3,4,5,后面都初始化为0
int arr2[3][4] = {
{
1,2},{
3,4},{
5} };
//指定行初始化
int arr3[][4] = {
1,2,3,4,5 };
//这里是有两行三列
//二维数组初始化时,行可以省略,但是列不可省略
return 0;
}
我们调试起来看一下:
2.3 二维数组的使用
二维数组的使用也是通过下标的方式。
代码如下:
//按行打印
#include <stdio.h>
int main()
{
int arr[3][4] = {
{
1,2},{
3,4},{
5} };
int i = 0;//行
for (i = 0; i < 3; i++)
{
int j = 0;//列
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
//按列打印
#include <stdio.h>
int main()
{
int arr[3][4] = {
{
1,2},{
3,4},{
5} };
int j = 0;
for (j = 0; j < 4; j++)
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
2.4 二维数组在内存中的存储
#include <stdio.h>
int main()
{
int arr[3][4] = {
{
1,2},{
3,4},{
5} };
int i = 0;//行
for (i = 0; i < 3; i++)
{
int j = 0;//列
for (j = 0; j < 4; j++)
{
printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
}
}
return 0;
}
- 观察输出结果,我们可以看到每个地址和一维数组一样都是相差4;其实二维数组在内存中也是连续存储的。
- 我们将二维数组想象成一维数组,二维数组的第一行为arr[0][j],第二行为arr[1][j],第三行为arr[2][j];我们就可以把arr[0],arr[1],arr[2]分别作为三个一维数组的数组名。
3. 数组越界
数组的下标是有范围限制的。
数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查。
二维数组的行和列也可能存在越界。
示例1:
#include <stdio.h>
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 ", arr[i]);
}
return 0;
}
运行结果:
我们这里运行出来没有报错,但是我们要知道我们的代码错了,下标为10 的数是什么我们不知道,这里访问的不是我们数组里面的数了,这里就形成了越界访问。编译器也有可能没有发现这个bug,编译器没有报错,不代表它是对的。
编译器在这里报了警告:
示例2:
#include <stdio.h>
int main()
{
int arr[3][4] = {
1,2,3,4,5,6,7,8,9,10,11,12 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
运行结果:
我们的代码越界访问了,跨行越界,一行访问了五个元素,直接混乱了。
每一行在打印的时候都会相后越界一个元素,到第三行的时候向后越界访问的不是我们数组里面的数了。
4. 数组作为函数参数
往往我们在写代码的时候,会将数组作为参数传个函数,比如:我要实现一个冒泡排序(这里要讲算法思想)函数将一个整形数组排序。
冒泡排序参考这篇博客:【C语言】冒泡排序
5.数组名
数组名是什么?
数组名本质是:数组首元素的地址。
因为数组名本质是:数组首元素的地址。
所以数组作为函数参数的时候,不是把整个数组的传递过去。
数组在传参的时候,传递的是首元素的地址,那么数组的形参就应该是指针变量来接收。
代码
#include <stdio.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int i = 0;
printf("%p\n", arr);//arr是数组名,数组名是首元素地址
printf("%p\n", arr+1);//首元素地址+1
printf("%p\n", &arr[0]);//&arr[0]存的是首元素地址
printf("%p\n", &arr[0]+1);//首元素地址+1
printf("%p\n", &arr);//数组的地址
//&arr,取出的是数组arr的地址,但是取出的地址也是首元素的地址,但是本质不一样。
printf("%p\n", &arr + 1);//数组的地址+1
//这里数组地址+1,加的不是4,而是40
printf("%d\n", *arr);
//数组首元素地址对应的元素
printf("%p\n", &arr + 1);
//取出数组arr的地址,指针指向这个数组的下一个元素
printf("%p = %p\n", p + i, &arr[i]);
//p+i=arr[i]
//p+i 就是下标为i元素的地址,通过对p+i解引用,找到下标为i的元素
printf("%d\n", sizeof(arr));
//sizeof(arr),arr表示整个数组
return 0;
}
运行结果:
补充:
- sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
- &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。
- 除此1,2两种情况之外,所有的数组名都表示数组首元素的地址。
本章到这里就结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话请给个三连支持一下吧!
Fighting!!!✊