曾经学习C言语的时分觉得数组和指针结合在一起的时分真的是阴间,很简单就搞混杂了,最近看了C言语深度解剖有了一点了解,好好的总结一下吧。其间许多的常识都是因为咱们在学习的进程中没有细心的去剖析导致的。一起我领会到了咱们在写代码的进程中应该更多的重视代码的调试,而不是换新的代码,只要不断的调试才干知道其间问题地点。
数组和指针之间原本没有什么联系,数组便是数组,指针便是指针,之间并没有联系,仅仅因为某些相似特性使得咱们在剖析的进程中存在较大的利诱。
数组便是一个接连存储空间的存储的数值。指针便是指针,指针变量地点内存中存储的值都是地址。
C 言语中数组的巨细有必要是一个常数,可是不能以为选用const限制的变量就能作为数组巨细的值,在C言语中const并不是界说一个常数,仅仅界说了一个只读类型的数据,并不是常数。在C言语中一般选用:
#define N 5
int Array[N];
const int M = 5;
int Array[M];//这是一种过错的界说方法,留意const并不是界说常量,可是C++中能够这样界说。
在一维数组中运用下标来拜访数组,Array是整个分配存储空间的姓名,单个的存储空间并没有姓名。这个存储空间存储的值为数组的元素,首要是 Array[0],Array[1],…,Array[N-1],数组元素并没有姓名。咱们对数组的拜访首要是选用下标的方法进行拜访。Array与这块存储空间现已亲近的相关起来,不能改动。可是需求首要的是Array作为右值时,表明的是该数组首个元素(Array[0])的地址,而不是代表整个数组的地址开始地址,尽管两个开始地址是相同的,可是需求理清其间的概念。整个数组的开始地址能够经过对这块存储区域取地址,也便是选用&a,这时得到的值才是整个数组的开始地址,尽管两个值是相同的,可是需求搞清楚其间的道理。
在数组中方位的改变也是非常重要的。
int *p = NULL;
p = Array + 1;
是指在数组Array的首元素的首地址上添加一个元素的宽度,使得p指向Array[1]。因为Array表明的是数组首个元素的首地址,那么操作的最小单位便是元素,Array + 1便是拜访下一个元素。
p = &Array + 1;
因为&Array是表明数组的开始地址,操作的最小单位是一个数组,而不是元素,因而Array+1便是下一个数组的开始地址,也便是将p指向了Array的下一个数组,而不是元素。
因而需求留意一维数组名在作为右值时是表明数组首个元素的首地址,并不表明整个数组的首地址。
关于二维数组(多维数组)也存在相似的问题。
int *p = NULL;
#define N 5
#define M 5
int A[N][M];
数组A存在5个元素,每一个元素是一个数组,每个数组中存在5个元素。数组名A表明首个元素的首地址,因而A表明A[0]的首地址,A的元素为数组,A + 1表明下一个元素(小数组)也便是A[1]的首地址;&A表明整个数组的首地址。&A+1表明下一个数组的开始地址。
A作为右值时,表明数组首元素的首地址,也便是A[0]的首地址,A[i]作为右值时是表明第i个元素(也是数组)的首元素(A[i][0])的首地址。因为A表明地址,这与指针存在许多的相似性,因而可选用指针的方法进行拜访。详细的完成进程如下:
A %%第0行的元素的首地址,不是第0行第0个元素的地址
A[i] <---> *(A+i); %%第i行第0个元素的地址
A[i][j] <---> *(*(A+i)+j); %%第i行第j个元素
&A[i][j] <---> (*(A+i)+j); %%第i行第j个元素的地址
A[i]+j <---> (*(A+i)+j); %%第i行第j个元素的地址
&A+1 %%下一组数组的开始地址
A+i %%第i行数组的开始地址
在多维数组中存储方法是线性的存储方法,能够经过指针快速的拜访。首要A表明首元素的首地址,将指针指向这个首地址就能快速的完成拜访。
为了阐明这些相互联系的,选用GDB对数组进行调试:
int main()
{
int i = 0,j = 0;
int a[5][5];
int b[5]={4,5,7,8,9};
for(;i < 5; ++i)
for(;j<5;++j)
a[i][j] = (i-j)+15;
}
编译调试:
(gdb) p a
$1 = {{15, 14, 13, 12, 11}, {32768, 8736756, 8729060, -1073744844,
-1073745080}, {0, -1073744928, 134518436, -1073745064, 134513340}, {
7298965, 134518436, -1073745016, 134513785, 134513194}, {8740000, 8740000,
8736756, 134513760, 134513408}}
(gdb) p &a
$2 = (int (*)[5][5]) 0xbffff314
(gdb) p &a+1
$3 = (int (*)[5][5]) 0xbffff378
(gdb) p a+1
$4 = (int (*)[5]) 0xbffff328
(gdb) p *(a+1)
$5 = {32768, 8736756, 8729060, -1073744844, -1073745080}
(gdb) p *(a+1)+1
$6 = (int *) 0xbffff32c
(gdb) p *(*(a+1)+1)
$7 = 8736756
(gdb) p *a
$8 = {15, 14, 13, 12, 11}
(gdb) p **a
$9 = 15
(gdb) p **(a+1)
$10 = 32768
(gdb) p a[1]
$11 = {32768, 8736756, 8729060, -1073744844, -1073745080}
(gdb) p *a[1]
$12 = 32768
(gdb) p a[1]+1
$13 = (int *) 0xbffff32c
(gdb) p *(a[1]+1)
$14 = 8736756
依据$2 = (int (*)[5][5]) 0xbffff314中的[5][5]可知&a的巨细是5*5的空间,刚好是一个数组的巨细,阐明&a表明整个数组的开始地址。(gdb) p &a+1 $3 = (int (*)[5][5]) 0xbffff378 中的[5][5]可知&a+1也是一个数组的开始地址,这也阐明晰&a是整个数组的初始地址。
(gdb) p a+1,$4 = (int (*)[5]) 0xbffff328中的[5]阐明a+1是元素a[1](数组5个元素)的开始地址,而不是某一个数值(二维元素)的开始地址。
(gdb) p *a,$8 = {15, 14, 13, 12, 11},(gdb) p **a,$9 = 15,$8,$9阐明a表明的是元素a[0](一个小数组)的开始地址,而不是某一个值的开始地址。*a是一个值(二维元素)的开始地址。
(gdb) p a[1]+1 $13 = (int *) 0xbffff32c 阐明a[1]+1是一个值的地址,而不是一个数组的开始地址,因而a[i]+j是一个值(二维元素)的地址。
(gdb) p *(a+1)+1 $6 = (int *) 0xbffff32c (gdb) p *(*(a+1)+1) $7 = 8736756,依据调试成果可知*(a+1)+1是一个值的地址。因而*(a+i)+j是一个值(二维元素)的地址。一起可知*(*(a+i)+j)是一个值(二维元素)。a[i][j]也是一个值。
归纳上面的剖析可知数组名A在作为右值时是数组首个元素(可能是一个值也可能是一个数组)的开始地址,而&A表明整个数组的开始地址。二维数组详细问题主张多去调试,依据调试剖析其间的含义。多调试,多调查,多了解。