跳转至

指针与二维数组

二维数组的基本元素是若干个一维数组。因此,指向二维数组基本元素的指针用一维指针。相应地,指向二维数组最小元素(指向一维数组的元素)的指针用零维指针

一维指针存储着二维数组的行地址。每个行地址指向对应行的入口处,访问这个行地址可以得到这一行首元素的地址;

也就是说行地址指向的值也是一个地址,但这个地址是这一行的第一个列地址

装饰器1
二维数组的行地址和列地址

{% raw %}int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};{% endraw %}

for(int i=0; i<3; i++){
    for(int j=0; j<4; j++)
        cout<<a[i][j]<<' ';
}
cout<<endl;

for(int i=0; i<3; i++){
    for(int j=0; j<4; j++)
        cout<<*(*(a+i)+j)<<' ';
}
cout<<endl;
  • 数组名a[行下标][列下标]可以访问二维数组最小元素

  • *( *(行首地址a+行偏移量i) + 列偏移量j)也可以访问二维数组最小元素:

行地址(a+i)指向的是第(a+i)列首地址,所以二维数组最小元素也可以理解如下: $$ *( (某一列首地址) + 列偏移量j) $$ 其中“某一列首地址”为 $$ *(行首地址a+行偏移量i) $$

注意,数组名a在这里表示&a[0],是一个一维指针,指向第一个行地址。(而不是&a[0][0]


使用行指针(一维指针)

1
2
3
4
5
6
7
8
{% raw %}int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};{% endraw %}
int (*p)[4];
p=a;        //等效于p=&a[0]

for(int i=0; i<3; i++){
    for(int j=0; j<4; j++)
        cout<<*(*(p+i)+j)<<' ';
}

p被指向了第一个行地址&a[0]

注意定义行指针int (*p)[4]和定义指针数组int *p[4]的不同:

  • int (*p)[4]:这是 指针 的定义。 语法:int [4]决定了指向类型为“包含4个整数的数组”,p决定了变量名为p,*作为特征标识符表示p是一个指针

    这里的括号()确保了*p结合(表示正在定义的p是一个指针),这样int[4]共同表示p指向的类型为int [4],也就是指向一个一维数组。

    直观上来说,(错误的)int *(p[4])更能表达”指向数组的指针“这一属性,但由于[]同时用于数组访问和数组定义,(错误的)int *(p[4])会被理解为试图将一个指针类型int *赋值给一个数组元素p[4],因此报错“[Error] conflicting declaration int* p [4]

  • int *p[4]:这是 数组 的定义。 语法:int试图决定数组元素的类型为整型,而*作为特征标识符进一步修正了数组元素的类型为“整型指针变量”;p决定了数组名为p,[4]决定了数组元素数量为4

    这里没有括号(),方括号[]是最高优先级,p[4]结合(表示正在定义的p是一个数组),这样前面的int *才能共同表示数组元素的类型为“整型指针变量”


使用列指针(零维指针)

int a[3][4];
int *p;
p=a[0];        //等效于p=&a[0][0]或p=*a(只访问了一层)  
for(int i=0; i<3; i++){
    for(int j=0; j<4; j++)
        cin>>*(p++);
}

p=a[0];
for(int i=0; i<3; i++){
    for(int j=0; j<4; j++)
        cout<<*(p++)<<' ';
}

p被指向了第1行的第1个列地址a[0]*a&a[0][0](即整个数组a的首元素),这也体现了零维指针是指向一个数的