二维数组和指针

二维数组和指针,是一个比较难的地方,能力有限,也只能谈谈自己对此略微的认识,如有啰嗦口误之处,多多包涵!!

推荐读者先看我的另外两篇文章 指针 http://www.cnblogs.com/wangweigang/p/8990237.html  数组和指针http://www.cnblogs.com/wangweigang/p/8994743.html

新学一个东西,关键是搞懂它是怎么用的,在会用的基础上再对它进行理解,才能运用自如,怎么用只要严格按照样式来,不费脑子,首先先定义,看代码:

 1 int a[3][3];

2 int (*p)[3];

3 p = a; 三句话,就把一个指针指向了二维数组,第一句话,告诉编译器,给我来一片空间,3*3的int类型的,好这就有内存空间了,有内存空间才有指针,所以每次用指针必须搞懂我这指针指的是哪片空间,不然很容易出错,改也不好改,好空间有了,该有指针了,我这个指针,想指向刚申请的那片空间,也就是想指向a,好想指向a那就得知道a是什么类型。a一看 int? 那就打错特错了,a的类型是 int [3] 注意,这个3是第二个3 如果a是 int a[3][4] 那么 a的类型就是 int [4] 为什么是这样呢,因为,所谓二维数组,就是每个元素都是一维数组的数组,就是说,我这个二维数组a 就是有三个元素的数组,只不过这三个元素的类型每个都是一个一维数组。看图

 

从上图就可很清楚的看出二维数组的空间排列方式,因为指针是用来操作空间的,所以想把指针用好,必须很熟练的理解每种存储类型的存储方式,然后才能灵活操纵指针.现在来看,要想用p指向a就得判断a的元素的类型,很显然,a的类型是int [3] 表示a的每个元素都是长度为3类型为int的数组。这可以想着指针指向一维数组来理解,因为一维数组的每个元素都是int所以指向int的指针就可以指向一位数组。这样在上面代码的第二行就可以定义出能指向a这个二维数组的指针,我们来分析分析这个定义语句,int (*p)[3];     首先 看变量名p他和 一个*用圆括号扩在一起,就表明他先和*结合表示它是一个指针,记住,圆括号千万不能少,因为方括号比*的运算优先级高,没有圆括号p会先和[3]结合表示p是个数组,这和本来的用意不符,所以下面把a赋值给p就会出错。好先表明p是一个指针,那么他指向什么的,往外看,注意 外面这个int [3] 是一个类型,表示一个数组长度为3每个元素都是int ,就算int [4] 和int [3]都不是一个类型,因为,区分数组的是 长度,类型,和数组名。长度不同的数组肯定不是一个数组,类型不同的肯定也不是一个。所以在定义时候,每个括号都不能少,每个数组都不能错。最后一句把a赋值给p a其实就是数组的地址。所以现在 p存的a的地址,p指向了a。这就定义完成,并且在数组和指针之间建立了联系,这样,这片3*3的内存空间可以通过a来操作,也可以通过p来进行操作,那么如何来进行操作呢,看代码

//接着上面的代码。已经定义好a和p并建立的联系
a[1][1] = 2;//将2赋值给a[1][1],通过数组名和下标的方式来操作数组
*(*(p+2)+1) = 3;//相当于把p赋值给a[2][1]
*(*(a+1)+1) = 4; //和 a[1][1]效果相同
p[2][1] = 4; //和*(*(p+2)+1)效果相同

 

这些代码中,a[1][1]肯定很好理解,就是操作坐标是(1,1)位置的元素,记住,数组是从0开始的。下面几句语句不好理解,可以想到,在一维数组时候,a[1] 和*(p+1) 操作的是一个空间。就好想把方括号换成了星号加圆括号,具体为什么等会儿再讲,请读者现将上面的牢牢记住,这就是利用指针来操作数组,在写代码和考试的时候,都在可以在出现a[i][j]的地方换成*(*(a+i)+j) 或者出现*(*(p+i)+j)的地方换成p[i][j] 。其实,编译器在编译你的代码的时候,就是把数组下标的形式转化成了指针的形式,如果你用*(*(a+i)+j) 而不用a[i][j] 只是帮编译器完成了一步工作而已,所以你在实际写代码的时候,两种方式都可以运用,看个人习惯,不用非得数组就是下标的形式,指针就是*加圆括号的形式,好下面讲为什么

 

从这张图片可以清楚的看出指针和数组他们代表的位置,从指针的角度讲,指针p先开始指向了数组开始,然后p就是数组的地址 *p就是进去了一层,即第一行,相当于*(p+0),0可以省略。。。但最好写上,然后*(p+1)就是讲指针往后移了一个单位,然后再进去,就相当于第二行。类推,*(p+2)就相当于第三行,此时,*(p+i)就是第i+1行,他里面存的就是第i+1行第一个元素的地址,然后在用解地址符即*就可对此位置的元素进行操作,即**(p+0) = 2就是第一行第一个元素,坐标为(0,0)的 ,就将2赋值给它,同样等价与*(*(p+0)+0) = 2; 那么可以理解 *(p+i)+j 就是第i+1行第j+1列的元素的地址,而再加个*就是*(*(p+i)+j)就是这个元素了。圆括号一个都不能少,因为+运算优先级比*低,要是去掉是先解地址再对地址所代表的空间进行操作,而不是对指向这个空间的指针进行操作。可能这一段有些绕,希望读者可以用一些代码来练习练习,通过不同的操作输出的结果来分析这个操作的含义,是否和你想要的结果有所差距,这里我列出一部分代码 读者亦可以尝试运行并加以分析

#include<stdio.h>
int main(void)
{
    int a[3][4]={{1,2,3,4},{6,7,8,9,11},{12,13,14}}//意思就是有一个元素个数为三个的一维数组他们的内容为别是a[0]={1,2,3,4} a[1]={6,7,8,9} a[3]={11,12,13,14}
    printf("%d,%d\n",a,*a);  //0行首地址和0行0列元素地址
    pritnf("%d,%d\n",a[0],*(a+0));//0行0列元素地址
    printf("%d,%d\n",&a[0],&a[0][0]);//0行首地址和0行0列元素地址
    printf("%d,%d\n",a[1],a+1);//1行0列元素地址和1行首地址
    printf("%d,%d\n",&a[1][0],*(a+1)+0);//1行0列元素地址
    printf("%d,%d\n",a[2],*(a+2));//2行0列元素地址
    printf("%d,%d\n"&a[2],a+2);//2行首地址
    pritnf("%d,%d\n",a[1][0],*(*(a+1)+0));//1行0列的元素的值
    pritnf("%d,%d\n",*a[2],*(*(a+2)+0);//2行0列元素的值
    
    return 0;
}

这个代码是指针哪章里面的 在那里面粗略的提到了二维数组和指针,那是我当时学习的时候的笔记。没有讲很详细。读者可以尝试分析上面代码 ,可能感觉头晕,但是记住,分析代码的时候,要根据我上面画的那个矩阵来分析,或者你们可以在纸上把这个矩阵列出来,然后在分析这些代码 不用只靠脑子想,脑子想想就糊涂了

下面讲讲二维数组做参数的传参问题,我在指针和数组中说过,数组做函数参数,会变成指针,好,现在先用个程序来示例

#include<stdio.h>
#define M 3
void scan(int (*a)[M],int n)
{
    int i,j;
    for( i = 0; i < n; i++){
        for(j = 0; j < n; j++){
            scanf("%d",&*(*(a+i)+j));
        }
    }
}
void transpose(int (*a)[M],int n)
{
    int i, j, t;
    for( i = 0; i < n; i++ ){
        for( j = 0; j < n; j++ ){
            if(i == j)
                break;
            else
            {
                t = *(*(a+i)+j);
                *(*(a+i)+j) = *(*(a+j)+i);
                *(*(a+j)+i) = t;
            }
        }
    }
}
void print(int (*a)[M],int n)
{
    int i,j;
    for(i = 0; i < n; i++){
        for(j = 0; j < n; j++){
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }
}
int main(void)
{
    int a[M][M];
    scan(a,M);
    transpose(a,M);
    print(a,M);

    return 0;
}

这个程序是实现矩阵的转置,分别用是三个函数对矩阵进行输入,输出和转置。这里面用了宏定义,不理解的就把所有的大写M换成3就行。这个三个函数的参数都有一个二维数组,好,看每个函数的形参,他是一个指向二维数组的指针,所以实参都是a这个二维数组,这个a不能取地址,因为他本身就是二维数组的地址。把a传过去以后,每个形参指向了这个数组,但是通过形参只知道每行有多少列,不知道有多少行,所以需要另一个参数代表有多少行。在每个函数中,我既可以用指针加*和圆括号的形式,也可以用方括号下标的形式来对数组进行操作,只要记住两点,1:数组从0开始,n-1结束,即数组的界限 2:里数组名或这指针名最近的代表的是行,远的代表的是列。熟练运用数组和指针两种形式的转换,就不会再因为指针方法表示看着很复杂,很晕的情况。还有在定义函数时候,形参指向二维数组,可以用指针的方式定义,如上 int (*a)[m], 也可以 int a[][m] 第一个方括号里面可以写也可以不写,因为编译器会把 int a[][m]的形式转化成 int (*a)[m]的形式,如果写成int a[1][m] 这个1会被丢掉,没有任何意义,所以在定义函数形参的时候 这两种方式也看个人爱好,除非题上强制要求。好 指针和二维数组先到这里 以后发现有误或者缺的地方再补

原创。转载标明出处,谢谢!~

猜你喜欢

转载自www.cnblogs.com/wangweigang/p/9031451.html