13、指针与多维数组

  

1、数组排列

  二维数组可看作一维数组的堆叠,如图所示。每个一维数组在内存中都是线性排列的。

  如图所示,首先排第0行,接着排第一行,以此类推。

下面个代码演示了如何访问二维数组。代码中使用<变量名>[行][列](data[i][j])表达式。

#include <stdio.h>
#include <string.h>
int main()
{
    int data[5][5];
    int i,j;
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
            data[i][j] = -1;
        }
    }
}

2、多维数组指针

(1)一维数组语法

  数组变量名

  假定一个int型一维数组名为arr[10];指定的数组变量名等价于第0个元素的地址。

arr=starting of the 0th element

  数组运算

  <数组变量名>+偏移量

  该表达式的结果为与第0个元素偏移某个距离后对应元素的地址。

  下标符号的概念

arr[i]==i[arr]==*(arr+i)

 

(2)二维数组语法

  数组变量名

  假定int型变量arr[5][5],则变量名arr为数组变量名。对于二位数组,制定数组名时会创建第0行的地址(一维数组),接着变量名加偏移量得到第i行的起始地址。可以看到下面代码段会得到同样的结果。

 

#include <stdio.h>
#include <string.h>
int main()
{
    int data[5][5];
    int i,j;
    int count = 0;
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
            data[i][j] =count++ ;
        }
    }
    printf("数组开始的地址为%p\n", data);
    for (i = 0; i < 5; i++)
    {
        printf("%dth row location =%p\n", i, data[i]);
        printf("Loc %d,1=%p\n    ", i,&data[i][0]);
    }
    return 0;
}

运行结果为:

  上面代码中,通过变量名和第一个索引(data[i])访问每一行(本身为一维数组)。

  因此,表达式<变量名>+索引==<变量名>[索引]将得到第i行的起始地址。

实例:

arr+i==arr[i]

 数组地址运算

   对于表达式<变量名>[i]+偏移量,偏移量与数组元素类型一致。由于这里是一个整型数组,因此加4个字节到数组基地址,最后跳到同一行的第2列的位置。下面通过示例对上述表达式访问各行的每一列进行进一步理解。

 

#include <stdio.h>
#include <string.h>
int main()
{
    int data[5][5];
    int i,j;
    int count = 0;
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
            data[i][j] =count++ ;
        }
    }
    for (i = 0; i < 5; i++)
    {
        printf("%dth row location =%p\n", i, data[i]);
        printf("columns\n");
        for (j = 0; j < 5; j++)
        {
            printf("%d=%p        ", j, data[i]+j);
        }
        printf("\n");
    }
    return 0;
}

 运行结果如下:

位置的值

   我们通过下述表示式访问二维数组第i行第j列的值:

    <数组变量名>[行][列]

   其中行和列为下标

例如

arr[i][j];

  当然也可以用指针符号指定它。因为arr[i]==第i行的地址,如果将索引j加给它就会调到改行第j列(arr[i]+j)。因此可用&arr[i][j]或(arr[i]+j)得到arr[i][j]元素的地址。

  我们使用上面两个表达式的“取值”操作符得到第i行第j列的值。例如*(&arr[i][j])或*(arr[i]+j)。所以我们得到表达式1

arr[i][j]==*(&arr[i][j])或*(arr[i]+j)--

  另外,在一维数组中,arr[i]=&arr[i]的地址或(arr+i)第i个位置的值都可通过下面的表达式2得到

arr[i]==*(arr[i])==*(arr+i)  

  所以,利用表达式2替换表达式1可得到:

*(arr[i]+j)==*(*(arr+i)+j)

  在处理多维数时,编译器解释该表达式会有一个微小差异。对于一维数组,*(arr+i)会得到第i个索引的值,而对于二维数组,表达式同样会得到第i行的地址。

  通过前面可以知道,二维数组是一个一维指针的堆叠,可用下图表示:

  我们能用指针数组访问数组元素。对于int arr[5][5];也可写作int (arrptr)[5]=arr;

示例:得到一个一维数组的基地址。

#include <stdio.h>
#include <string.h>
int main()
{
    int data[5][5];
    int i,j;
    int count = 0;
    int (*aptr)[5];
    int *dataptr;
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
            data[i][j] =count++ ;
        }
    }
    aptr = data;
    for (i = 0; i < 5; i++)
    {
        printf("%dth row =%p\n", i, *aptr++);
    }
    return 0;
}

结果为:

 

 

   在上面的代码中,指针数组用于指向每行(即*aptr)的基地址。

 

示例:利用指针变量数组访问二维数组的值。

#include <stdio.h>
#include <string.h>
int main()
{
    int data[5][5];
    int i,j;
    int count = 0;
    int (*aptr)[5];
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
            data[i][j] =count++ ;
        }
    }
    aptr = data;
    for (i = 0; i < 5; i++)
    {
    for (j = 0; j < 5; j++)
        {
            printf("%d,%d=%p  val=%d\n",i, j, (*aptr+j),*(*aptr + j));
        }
        printf("\n");
        aptr++;
    }
    return 0;
}

程序运行结果如下:

 

   在上面的代码中,指针指向每行的基地址,就可将第2个索引作为偏移量访问数组的各个元素。所以(*aptr+j)会得到第i行和第j列的地址。*(*aptr+j)会得到第i行第j列的地址的值。

 

通过指针变量访问二维数组的索引

  这里是利用指针变量访问二维数组索引。这和如何利用指针变量访问一维数组的方法相似。为此需将每行的地址赋值给指针变量。

#include <stdio.h>
#include <string.h>
int main()
{
    int data[5][5];
    int i,j;
    int count = 0;
    int (*aptr)[5];
    int *dataptr;
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
            data[i][j] =count++ ;
        }
    }
    aptr = data;
    for (i = 0; i < 5; i++)
    {
        printf("Address of %d row =%p\n", i, (*aptr+i));
        dataptr = (*aptr + i * 5);
        
        for (j = 0; j < 5; j++)
        {
            printf("%d,%d=%p    val=%d\n        ",i, j, dataptr,*(dataptr));
            dataptr++;
        }
        printf("\n");
    }
    return 0;
}

 程序运行结果:

  上面代码中,每一行的地址被分配给整数指针int *dataptr.

dataptr=(*aptr+i*5);

  然后通过*(dataptr)和dataptr++累加得到该行索引的值,直到每行结束。

 

(3)三维数组排列

  类似的,三位数组也可看作数组的堆叠,但有一个例外,这里每个堆叠元素为一个二维数组。在线性图中,三维数组排列如下:

  三维数组基础

  指定三位数组需要三个索引,如下所述。假定三维数组元素为整数,其大小为5,5,5,定义为:

int data[5][5][5];

示例:下面代码演示了如何通过三维数组元素。

 

#include <stdio.h>
#include <string.h>
int main()
{
    int data[3][3][3];
    int i,j,k;
    int count = 0;

    for (i = 0; i <3 ; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                data[i][j][k] = count++;
            }
        }
    }
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                printf("%d%d%d=%d    ", i, j,k, data[i][j][k]);
            }
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}

程序运行结果如下:

  例子中三维数组结构如下:

理解三维数组的表达及含义

   假定一个三维数组声明为int data[5][5][5]。该数组变量名创建第0行的地址。由于三维数组的每行都包含一个二维数组,这也是一维数组第0行的地址,即一维数组(0,0,0)第0个元素的地址。

 

#include <stdio.h>
#include <string.h>
int main()
{
    int data[3][3][3];
    int i,j,k;
    int count = 0;

    for (i = 0; i <3 ; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                data[i][j][k] = count++;
            }
        }
    }
    printf("0th row of 3d array=%p\n", data);
    printf("0th row of 2d array=%p\n", data[0][0]);
    printf("0th row of 1d array=%p\n", &data[0][0][0]);
    return 0;
}

运行结果如下

  这个示例可以得出以下结论:

<变量名>==三维数组第0行的地址
<变量名>[0][0]==二维数组第0行的地址
&<变量名>[0][0][0]==一维数组第个元素的地址
他们的表示的是同一个元素的地址。

数组运算

  下面表达式的结果为与第0个元素距离某个偏移量位置元素的地址。

<变量数组名>+偏移量

  处理三维数组时有三层间接寻址。在第一层上,可以看到三维数组以二位数组形式排列。因此,如果该层上加个偏移量,他会加二维数组大小的偏移量。

 

#include <stdio.h>
#include <string.h>
int main()
{
    int data[3][3][3];
    int i,j,k;
    int count = 0;

    for (i = 0; i <3 ; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                data[i][j][k] = count++;
            }
        }
    }
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                printf("%d%d%d=%d  addr %p    ", i, j,k, data[i][j][k],&data[i][j][k]);
            }
            printf("\n");
        }
        printf("\n");
    }
    printf("Index value address\n");
    for (i = 0; i < 3; i++)
    {
        printf("row %d addr=%p\n", i, data + i);
    }
    return 0;
}

程序运行结果:

  如上输出结果所示,索引0,0,0的值为第0行的地址,同样100为第1行地址,200为第二行的地址。

  表达式data[i]与data+i等效,都能得到二维数组的第i行的索引。

  第二层上,我们发现二维数组为一维数组的堆叠。通过解引用第一层表达式得到第二层,所以*(data[i]+i)与data[i][j]等效,都指向二维数组。而且,*data[i]得到三维数组每个元素的第i个二维数组的基地址。

#include <stdio.h>
#include <string.h>
int main()
{
    int data[3][3][3];
    int i,j,k;
    int count = 0;

    for (i = 0; i <3 ; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                data[i][j][k] = count++;
            }
        }
    }
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                printf("%d%d%d=%d  addr %p    ", i, j,k, data[i][j][k],&data[i][j][k]);
            }
            printf("\n");
        }
        printf("\n");
    }
    for (i = 0; i < 3; i++)
    {
        printf("row %d addr=%p\n", i, data[0][i]);
    }
    printf("2D row address\n");
    for (i = 0; i < 3; i++)
    {
        printf("3D %d ROW\n", i);
        for (j = 0; j < 3; j++)
        {
            printf("2D row %d addr=%p%p\n", j, data[i][j], *(data[i] + j));
        }
    }
    return 0;
}

运行结果:

  在第三层上,当解引用它时我们会看到一维数组及它们的值。通过解引用第二层表达式得到第三层,所以(*(data[i]+i)+k)与data[i][j]+k等效都指向一维数组元素。而要通过“取值"操作符得到这些地址存储的元素。

*((*(data[i]+i)+k))==*(data[i][j]+k)=d[i][j][k]

  下面通过一个例子演示如何使用上述表达式。

 

#include <stdio.h>
#include <string.h>
int main()
{
    int data[3][3][3];
    int i,j,k;
    int count = 0;

    for (i = 0; i <3 ; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                data[i][j][k] = count++;
            }
        }
    }
    printf("Index=val addr<>\n");
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                printf("%d%d%d=%d  addr %p    ", i, j,k, data[i][j][k],&data[i][j][k]);
            }
            printf("\n");
        }
        printf("\n");
    }
    for (i = 0; i < 3; i++)
    {
        printf("row %d addr=%p\n", i, data[0][i]);
    }
    printf("2D row address\n");
    for (i = 0; i < 3; i++)
    {
        printf("3D %d ROW\n", i);
        for (j = 0; j < 3; j++)
        {
            printf("2D row %d addr=%p%p\n", j, data[i][j], *(data[i] + j));
        }
    }
    printf("1D element address\n");
    for (i = 0; i < 3; i++)
    {
        printf("3D %d ROW\n", i);
        for (j = 0; j < 3; j++)
        {
            printf("2D %d ROW\n", j);
            for (k = 0; k < 3; k++)
            {
                printf("%d%d%d=%p  val=%d", j, j, k, *(data[i] + j) + k, *(*(data[i] + j) + k));
            }
            printf("\n");
        }
    }
    return 0;
}

运行结果如下:

 使用指针变量访问三维数组的每个元素

  访问三维数组元素与访问二维数组元素的技术是相同的。指针变量必须指向单维数组的基地址,也是每个二位数组的一部分。反过来,该二维数组是整个三维数组的单个元素。

示例如下:

#include <stdio.h>
#include <string.h>
int main()
{
    int data[3][3][3];
    int i,j,k;
    int count = 0;
    int *dataptr = NULL;
    for (i = 0; i <3 ; i++)
    {
        for (j = 0; j < 3; j++)
        {
            for (k = 0; k < 3; k++)
            {
                data[i][j][k] = count++;
            }
        }
    }
    for (i = 0; i < 3; i++)
    {
        printf("3D %d ROW\n", i);
        for (j = 0; j < 3; j++)
        {
            printf("2D %d row\n", j);
            dataptr = *(data[i] + j);
            for (k = 0; k < 3; k++)
            {
                printf("%d%d%d=%d  addr %p    ", i, j,k, dataptr,*dataptr++);
            }
            printf("\n");
        }
    }

    return 0;
}

运行结果如下:

  在上述代码中,表达式*(data[i]+j)得到第i个二维数组的行,该二维数组行指向第j个一维数组行的基地址。表达式dataptr=*(data[i]+j)中指针变量dataptr指向一维数组,我们可以通过解引用相同的*dataptr访问其值,也可食用指针变量自增操作符(即*dataptr++)遍历整个数组。

 

posted @ 2018-03-15 14:35  noticeable  阅读(1373)  评论(0编辑  收藏  举报