目录
前言
在编写程序时我们难免会遇到处理大量一系列类型相同数据的情况,如果使用以前学过的创建变量的知识,例如创建三个整型变量,代码如下:
int a = 0;
int b = 0;
int c = 0;
怎样做好像并没有什么太大问题,都是这里仅仅只让我们创建了三个,若实际问题需要我们创建几十个,上百个变量呢?这样一个一个创建肯定是不现实的,代码冗长不说,效率还低下。这时我们即将介绍的数组就发挥了很大的作用。
一、一维数组
1、一维数组的创建和初始化
数组是按顺序存储的一系列类型相同的值,如5个int类型的数值或10个char类型的字符。
1)数组的创建
通用方式:
type_t arr_name [const_n];
- type_t:指数组的元素类型(如int、char、float)
- arr_name:指数组的名称
- const_n:是一个常量表达式,用来指定数组的大小
数组创建举例:
//举例1
int arr1[10];
//举例二
char arr2[20];
//举例三
float arr3[10];
//举例四
double arr4[10];
一个特别容易错误的点:
有不少的小伙伴就会想:这时将10赋值给a,那么a就是10,这是非常容易陷入的误区。我们前面说到过[]中的内容需要为常量表达式,虽然a的值为10,但是a仍然是一个变量。那我们怎么修改才能是正确的呢?修改如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define a 10
int main()
{
int arr1[a];
return 0;
}
对于数组创建,在C99标准之前,[ ]中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组概念(数组大小待定的数组)。
2)一维数组的初始化
数组的初始化指的是,在创建数组的同时给数组的内容一些合理的初始值。其分为:完全初始化、不完全初始化、const_n处的省略。例子如下:
完全初始化:
//一、完全初始化
int arr1[5] = {1,2,3,4,5};
char arr2[3] = {'a',98,'c'};
//二、不完全初始化
int arr3[10] = {1,2,3} //后面未初始化的数字默认为0
char arr4[5] = {'a','b'};
//三、省略count_n部分——根据初始化来绝定数组大小
int arr5[] = {1,2,3,4};
char arr6[] = {'a','b','c'};
一个需要注意的点:字符与字符串
当我们省略数组大小时有如下代码:
char arr1[] = {'a','b','c'};
char arr2[] = "abc";
对于上面的代码我们需要分清它们在内存中分配的差别:我们使用visual studio 2019的监视功能来查看它们在内存中是如何分配的:
字符串在末尾会有一个‘\0’。
2、一维数组的使用
对于数组的使用我们使用一个操作符:[ ],下标引用操作符。它其实就是数组访问的操作符。
我们来看代码:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };//数组的不完全初始化
/*对数组内容赋值, 数组是使用下标来访问的,下标从0开始。
所以我们引入一个变量i作为下标*/
int i = 0;
for (i = 0; i < 10; i++)//这里写10,好不好?
{
arr[i] = i;
}
//输出数组的内容
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
对于一维数组的输入于输出我们常常使用for循环来进行。
数组大小的计算:
int arr[10];
int size = sizeof(arr) / sizeof(arr[0]);
代码解释:函数sizeof可以计算数据的大小,单位为字节。代码通过计算出数组总大小(单位为字节),再计算出单个元素所占字节大小,相除即可得出数组元素的多少。
3、一维数组在内存中的存储
接下来我们探讨数组在内存中的存储。我们前面学到过一个操作符为’&‘—取地址操作符,我们使用这个操作符来看看,数组是在内存中是如何存储的,见如下代码:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; ++i)
{
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
其输出结果如下:
由上图可知,随着数组下标的增长,元素地址也在有规律的增长。由此可以得出,数组在内存中是连续存放的。
总结:
1、数组在内存中是连续存放的
2、随着数组下标的增加,地址由低地址到高地址
3、数组下标从0开始,即0下标处访问的是数组的第一个元素
二、二维数组
1、二维数组的创建和初始化
1)二维数组的创建
int arr1[2][4];
char arr2[1][3];
double arr3[2][4];
通用方式如下:
type_t arr_name [const_n1][const_n2];
相较于一维数组仅仅是维数的差别。const_n1表示数组的行,而const_n2表示数组的列,如上述代码的第一行定义的arr1,表示定义了一个2行4列的二维数组。
2)二维数组的初始化
int arr1[2][2] = { 1,2,3,4 };
int arr2[2][2] = { {1,2},{3,4} };
int arr3[][4] = { {2,3},{3,4} };
对二维数组进行初始化时,行可以省略,但列不能省略,每个大括号内的数表示同一行。
2、二维数组的使用
二维数组的使用与一维数组一样,也是通过下标的方法。
#include <stdio.h>
int main()
{
int arr[3][4] = { 0 };
int i = 0;//下标
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
arr[i][j] = i + j;
}
}
//数组输出
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
}
return 0;
}
二维数组的输入与输出也需要通过for循环来实现,不过二维数组需要for循环的嵌套。
3、二维数组在内存中的存储
我们前面在二维数组的初始化时说到过二维数组是几行几列进行初始化的,那么二维数组在内存中的存放也是如几行几列存储在内存中吗?我们还是一探讨一维数组一样来进行对二维数组的内存进行探讨:
#include <stdio.h>
int main()
{
int arr[3][4];
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;
}
输出结果如下:
由上图结果可知二维数组在内存中也是连续存储的,并不是我们所理解的那样,可通过下图理解:
三、数组越界
通过前面的学习我们知道了数组的下标是有范围的,如果一个元素个数为n的数组,那么其下标最大值便为n-1。若数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以在写代码时,最好自己做越界的检查。
#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\n", arr[i]);//当i等于10的时候,越界访问了
}
return 0;
}
不仅仅是一维数组,二维数组也会出现行或列的越界。
四、数组作为函数参数
1、数组名
在探究数组作为函数参数前我们先来探讨另一个问题:数组名是什么?
我们先来看一段代码及其运行结果:
由上述结果可知:数组名是数组首元素的地址.
但有两个个例除外:
1、sizeof(数组名) — 这时数组名表示整个数组 — 计算的是整个数组大小,单位为字节
2、&数组名 — 这时数组名表示整个数组 — 取出的是整个数组的地址
2、数组作为函数参数
我们来编写一个简单的函数—打印数组以及输出该数组的大小:
错误的方法:
#include <stdio.h>
void printf_arr(int a[])
{
int sz = 0;//大小
int i = 0;
sz = sizeof(a) / sizeof(a[0]);
for (i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
printf("\n");
printf("数组元素个数为:%d\n", sz);
}
int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf_arr(arr);
return 0;
}
其运行结果为:
由输出的元素可得数组元素的大小不可能为1。那么错在哪里了呢?通过代码的调试可知printf_arr函数部分sizerof(a)错误,它计算的应该是整个数组的大小,但是实际上它的计算结果为4字节。这是因为数组传参时传的是数组首元素的地址,即形参a的本质是指针,即上述代码sizerof(a)计算的是指针的大小。即无法在函数内部求数组的大小,必须在主函数中实现。
正确代码为:
#include <stdio.h>
void printf_arr(int a[],int sz)
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
printf("\n");
printf("数组元素个数为:%d\n", sz);
}
int main()
{
int sz = 0;
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
sz = sizeof(arr) / sizeof(arr[0]);
printf_arr(arr, sz);
return 0;
}
其输出结果如下:
若有错误欢迎各位大佬指出,我们一起进步。