一、数组指针
数组指针本质是一个指针类型的变量,指向一个二维数组。
格式:
数据类型 (*数组指针变量名) [列宽] (列宽 == 指向的二维数组的列宽)
int arr1[2][3] = {{1,2,3},{4,5,6}};
int arr2[2][4] = {{1,2,3,4},{5,6,7,8}};
int (*p)[3] = arr1;
int (*q)[3] = arr2; //错误的
int (*q)[4] = arr2; //正确的
数组指针的应用:
在函数定义时,二维数组作为形参----------------row为行数,col为列数
返回值类型 函数名(数据类型 二维数组名[][列宽], int row, int col)
{
//代码块
}
可以使用数组指针代替二维数组作为函数的形参
返回值类型 函数名(数据类型 (*数组指针变量名)[][列宽], int row, int col)
{
//代码块
}
二、二维数组变量名与数组指针变量名的含义
int arr1[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = arr1;
其中,arr1 为二维数组名,同时也代表二维数组的首地址。
二维数组在内存中实际上是在连续的内存空间上存储的,可以通过一维数组的遍历方式进行遍历。也可以将二维数组当成几个一维数组来看待。arr1[2][3]就是由arr1[0]、arr1[1] 两个一维数组组成的,arr1的第一个元素不是arr1[0][0],而是arr[0]。数组名arr1代表的是arr[0]的地址,而不是arr1[0][0]的地址。
数组指针本质是一个指针,指向一个二维数组。
数组指针变量名 p 指向的是 arr1 数组的第一行的首地址,即arr[0]的地址,不是arr1[0][0],虽然arr[0]的地址与arr[0][0]的地址在数值上是一致的,但是其所表示的含义不同。p 指向的是一个由3个int类型的元素组成的数组的首地址arr[0],因此其在进行偏移时,p + 1增加了3 *sizeof(int) = 12个字节,而arr1[0][0]的地址在进行偏移时只会偏移一个数组元素的大小,增加了 sizeof(int) = 4个字节。因此,数组指针也称为行指针。
printf("p = %p\n", p);
printf("&arr1[0][0] = %p\n", &arr1[0][0]);
printf("p + 1= %p\n", p +1);
printf("&arr1[0][0] + 1 = %p\n", &arr1[0][0] + 1);
结果:
p = 0x7ffc2c0550b0
&arr1[0][0] = 0x7ffc2c0550b0
p + 1 = 0x7ffc2c0550bc
&arr1[0][0] + 1 == 0x7ffc2c0550b4
通过sizeof函数可以看出arr1[0]与arr1[0][0]所占空间的大小,所以在对行地址与行地址首元素进行偏移运算时,发现其地址的增量是不一样的。
printf("sizeof(p[0]) = %d\n",sizeof(p[0]));
printf("sizeof(p[0][0] = %d\n)",sizeof(p[0][0]));
//结果
sizeof(p[0]) = 12
sizeof(p[0][0] = 4
三、使用数组指针遍历二维数组
使用数组指针指向二维数组,可以将数组指针变量名当做二维数组名使用。
注意:在进行偏移量的计算过程中,通过观察前面的值是行地址,还是行首元素的地址。例如,p + 1, &arr1[0] + i 皆为行地址,偏移时以一行为单位进行计算。p[0] + 1,arr1[0] + i,*(p + i) + 1,*(arr + i) + 1皆为行首元素地址,偏移时以一个元素为单位进行计算,可以看出p[i] <==> *(p + i)。
通过二维数组名和数组指针访问二维数组中每个元素的值。
数组名[row][col] <==> *(数组名[row] + col) <==> *(*(数组名 + row) + col)
数组指针变量名[row][col] <==> *(数组指针变量名[row] + col)
<==> *(*(数组指针变量名 + row) + col)
//数组名方式
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
//printf("%d\n",*(*(arr1 + i) + j));
//printf("%d\n",*(arr1[i] + j));
printf("%d\t", arr1[i][j]);
}
putchar('\n');
}
//数组指针方式
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
//printf("%d\t", *(*(p + i) + j));
//printf("%d\n",*(p[i] + j));
printf("%d\n",p[i][j]);
}
putchar(10);
}
通过二维数组名和数组指针访问二维数组的每个元素的地址。
取二维数组每个元素的地址:
&数组名[row][col] <==> 数组名[row] + col <==> *(数组名+row) + col
通过数组指针变量指向二维数组的每个元素的地址:(数据类型 *)
&数组指针变量名[row][col] <==> 数组指针变量名[row] + col
<==> *(数组指针变量名 + row) + col
//数组名方式
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%p\t", &arr[i][j]);
//printf("%p\t", arr[i] + j);
//printf("%p\t", *(arr + i) + j);
}
putchar('\n');
}
//数组指针方式
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%p\t", &p[i][j]);
//printf("%p\t", p[i] + j);
//printf("%p\t", *(p + i) + j);
}
putchar('\n');
}
通过二维数组名和数组指针访问二维数组的每行首元素的地址。
取二维数组每行的首地址:
数组名[row] <==> *(数组名 + row)
通过数组指针变量指向二维数组的每行的首地址:(数据类型 *)
数组指针变量名[row] <==> *(数组指针变量名 + row)
//数组名方式
for (int i = 0 ; i < row; i++)
{
printf("%p\t", arr[i]);
//printf("%p\n", *(arr + i));
}
//数组指针方式
for (int i = 0 ; i < row; i++)
{
printf("%p\t", p[i]);
//printf("%p\n", *(p + i));
}
通过二维数组名和数组指针访问二维数组的行地址。
二维数组对应的行地址:
数组名 + row
通过数组指针变量指向二维数组的每行的地址:
数组指针变量名 + row
//数组名方式
for (int i = 0; i < row; i++)
{
printf("%p\t", arr + i);
}
//数组指针方式
for (int i = 0; i < row i++)
{
printf("%p\t", p + i);
}
四、总结
在使用数组指针指向二维数组时。有以下的等价关系
数组指针变量名 == 二维数组名 == &二维数组名[0]
二维数组名与数组指针变量名的异同点:
相同:二维数组名和数组指针变量名都是一个行地址,在使用时二维数组名和数组指针变量名的用法基本一致。
不同点:二维数组名是一个常量,不可以被修改,数组指针变量名是一个变量,可以修改数组指针的指向。