数组的个人理解:
文章内容主要是对一维数组和二维数组的介绍
一维数组:
1. 一维数组的创建和初始化:
一维数组的创建方式:
type_t arr_name [const_n];
//type_t ࣳ是指数组的元素类型
//const_n 是一个常量表达式,表示数组的大小。
//例子1
int arr[10];
//由于数组是由0,所以数组创建后可使用的最大单元为const_n-1,即0~const_n-1。
//上面创建的数组的长度为10,可以使用的单元为arr[0]~arr[9]。
//例子2
int tmp = 10;
int arr[tmp];
//这里创建会出现问题,因为tmp是一个变量,而[ ]中需要的是一个常量表达式,因此,创建会失败。
一维数组的初始化方式:
//一维数组的初始化
int arr0[5];
int arr1[10] = { 0 };
int arr2[5] = {};
int arr3[5] = { 1,2,3,4,5 };
int arr4[5] = { 1,2 };
char arr5[] = "abcde";
char arr6[5] = { 'a','b','c' };
char arr7[5] = { 0 };
char arr8[5] = { 32,48,59 };
//这些数组初始化后,数组中存储的值是什么呢?我们看下面的图片:
通过上面的图片我们可以看到,如果在定义数组时对数组不进行初始化,那么它的值就会是随机值。
如果对数组进行初始化,{}中出现的值,会依次从数组的首个元素开始初始化,如果有的数组成员未在{}中出现,那么它就会将其初始化为0。字符数字则默认初始化为‘\0’。
在创建数组时,如果不想直接指定数组的大小,则可以根据初始化的内容来确定。在初始化时,有两种方法。
char arr5[] = "abcde";
char arr10[] = { 'a', 'b', 'c' };
在进行以上两种方法的初始化时,需要注意数组的长度。
2. 一维数组的使用:
一维数组的使用,说白了就是对数组中各个成员的访问,对数组成员的访问,我们可以通过下标的方式,在访问时,只需要借助[ ]操作符就可以做到。
而在有时,我们需要使用指针的方式来访问数组,这个时候我们需要搞明白的问题就是,成员的存储方式是什么样的。
int arr3[5] = { 1,2,3,4,5 };
for (i = 0; i < 5; i++)
{
printf("&arr3[%d] = %p\n", i,&arr3[i]);
}
//定义一个数组,并对数组每个元素的地址进行输出
通过输出地址我们可以看出,数组的元素在存储时,是连续存储的。而我们都知道,数组名是数组首元素的地址,因此,我们只需要给这个地址加上一定的偏移值,就可以做到分别访问数组的各个成员。我们可以模拟出它在内存中的存储布局:
//举个例子
int arr3[5] = { 1,2,3,4,5 };
for (i = 0; i < 5; i++)
{
printf("arr3[%d] = %d ", i, arr3[i]);
printf("*(arr3 + %d) = %d", i, *(arr3 + i));
printf("\n");
}
// * 是解引用操作符,得到指针所指向的地址的内容。
通过输出的结果,可见,两种方式访问数组,得到的结果是一样的。
对于一维数组的操作还有一点需要我们注意
int arr3[5] = { 1,2,3,4,5 };
printf("arr3 = %p \n", arr3);
printf("&arr3 = %p \n", &arr3);
printf("(&arr3) + 1 = %p \n", (&arr3) + 1);
我们可以看到,当对数组名直接加一时,指针的偏移量是一个数组成员的字节大小,当对数组名取地址进行加一时,指针偏移量为整个数组的大小,也就是20个字节,所以,当对数组名取地址时,得到的地址虽然和数组名的地址相同,但是表示的意思却是完全不同的。
- 数组名表示的是数组首元素的地址,所以进行加一运算后,指针偏移了一个成员的字节大小。
- 数组名取地址表示的是数组的地址,所以进行加一运算后,指针偏移了整个数组的大小。
二维数组
二维数组的创建和初始化
//二维数组的创建
int arr[3][4];
char arr0[3][3];
//二维数组的初始化
int arr[3][4] = {1,2,3,4};
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3},{4,5}};
char arr[3][4] = {'a','b','c','d'};
二维数组的创建方式与一维数组相似,因此在这不做过多赘述,值得注意的是,虽然二维数组也可以在定义时不指定数组的大小,等数组初始化时通过参数再来指定,但对于行的大小却是必须在定义时指定清楚的。
对于二维数组的访问,同样也有两种,通过下标和通过指针,所以我们同样应该搞清楚二维数组在内存中的存储方式。
//创建一个二维数组,输出每个元素的地址
int arr[3][2] = { {1, 2}, {3, 4}, {5, 6} };
for (i = 0; i < 3; i++)
{
for (j = 0; j < 2; j++)
{
printf("&a[%d][%d] = %p ", i, j, &arr[i][j]);
}
printf("\n");
}
通过这段代码我们可以发现,二维数组在内存中,也是连续存储的,因此我们可以和访问一维数组那样,通过指针来访问。我们可以模拟画出它在内存中的存储布局。
int arr[3][2] = { { 1, 2 },{ 3, 4 },{ 5, 6 } };
printf("arr = %p\n", arr);
printf("arr + 1 = %p\n", arr + 1);
printf("&arr = %p\n", &arr);
printf("&arr + 1 = %p\n", (&arr) + 1);
printf("arr[0] = %p\n", arr[0]);
printf("arr[0] + 1 = %p\n", arr[0] + 1);
printf("&arr[0][0] = %p\n", &arr[0][0]);
printf("&arr[0][0] + 1 = %p\n", &arr[0][0] + 1);
printf("arr[2][1] = %d\n", arr[2][1]);
printf("*((arr[2]) + 1) = %d\n", *((arr[2]) + 1));
printf("*((arr[0] + 5)) = %d\n", *((arr[0] + 5)));
通过运行这段代码,我们可以看到数组名arr ,数组名取地址&arr, 首元素arr[0][0]地址, 以及第一行地址arr[0]的值都是一样的。但是当他们加一以后,地址的偏移量却是不同的,所以我们可以根据他们地址的偏移量来得出他们所表示的含义。
- arr ——数组名,arr+1,地址增加了8 。
printf("arr + 1 = %p\n", arr + 1);
printf("arr[1] = %p\n", arr[1]);
观察上面输出的截图,我们可以看到当arr + 1 后,它的地址变成了arr[1]的地址,也就是二维数组第二行整体的首地址地址,一次跳过了第一行的两个元素。因此我们可以把二维数组想象为一个新的一维数组,这个新的一维数组的每个元素就是二维数组的每一行,所以arr 表示的时 它的值的含义为 二维数组第一行的首地址,对它进行加减操作,地址的变化是以行为单位的。对它作以图解:
&arr ——数组名取地址,&arr+1,地址增加了24 ,也就是偏移了整个二维数组的大小。因此,它代表的时二维数组的地址,它的加减单位是整个二维数组。
arr[0] ——二维数组第一行的首地址,arr[0] + 1,地址增加了4 ,也就是偏移了第一行一个元素的字节大小的地址。因此,它代表的是把二位数组想象成一个新的一维数组,它就是这个新一维数组的首元素,对它加一,就是进到它内部来,求它包含的元素的地址。它的加减单位是每行的单个元素的大小。
&arr[0][0] ——二维数组首元素的地址,&arr[0][0] +1, 地址增加了4,也是一个元素的字节大小的地址。因此它代表的是二维数组中元素的地址,它的加减单位是元素的字节大小。
后面几个是根据这些情况对二维数组中的元素进行输出。