数组名实质&&二位数组与二维指针

 

二位数组与二维指针

闲话少说,先看一个程序:

//源码:

#include

#define SIZE(x)  sizeof(x)/sizeof(int)

#define printaddr(s,x) printf("addr-%s:%x\n",s,x)

#define printint(s,x) printf("Int-%s:%d\n",s,x);

int main()

{

     printf("Array a:\n");

     int a[5] = {1,2,3,4,5};

     int *test = &a;//waring Type not match!

     int (*p_OneDimensional)[5] =&a;

     int *p_Point = a;

     int *p_PointAnother = &a[0];

     printaddr("a",a);

     printaddr("a+1",a+1);

     printaddr("&a[0]+1",&a[0]+1);

     printaddr("&a+1",&a+1);

    

     printf("\n\n\nArray b:\n");

     int b[3][4]={

                             1,2,3,3,

                             4,5,6,6,

                             7,8,9,9

                        };

    

   printaddr("b",b);

     printaddr("*b",*b);

     printaddr("*b[0]",*b[0]);

     printaddr("&b+1",&b+1);

     printaddr("b+1",b+1);

     printaddr("&b[0]+1",&b[0]+1);

     printaddr("b[0]+1",b[0]+1);

     printaddr("&b[0][0]+1",&b[0][0]+1);

    

     printint("\n\n\nSize(*b)",SIZE(*b));

     printint("Size(*b[0])",SIZE(*b[0]));

     printint("Size(&b)",SIZE(&b));

     printint("Size(b)",SIZE(b));

     printint("Size(&b[0])",SIZE(&b[0]));

     printint("Size(b[0])",SIZE(b[0]));

     printint("Size(&b[0][0])",SIZE(&b[0][0]));

    

     int (*p_TB)[3][4] = &b;

     int (*p_SB)[4] = b;//or &b[0]

     int *p_PB = b[0];//or &b[0][0]

 

}

//输出:

Array a:

addr-a:bf932450

addr-a+1:bf932454

addr-&a[0]+1:bf932454

addr-&a+1:bf932464

 

 

 

Array b:

addr-b:bf932420

addr-*b:bf932420

addr-*b[0]:1

addr-&b+1:bf932450

addr-b+1:bf932430

addr-&b[0]+1:bf932430

addr-b[0]+1:bf932424

addr-&b[0][0]+1:bf932424

 

 

Int-Size(*b):4

Int-Size(*b[0]):1

Int-Size(&b):1

Int-Size(b):12

Int-Size(&b[0]):1

Int-Size(b[0]):4

Int-Size(&b[0][0]):1

 

    程序中只有int *test = &a;//waring Type not match!会有警告,其他全部正确。警告来自类型不匹配,因为&a的类型是int(*)[5]的。从程序中我们也可以看出对二维数组b,&b是int (*)[3][4]类型,a与&a[0]是

int (*)[4]类型,而a[0]与&a[0][0]是int *类型的。

    不过当计算SIZE时,我们会发现a,&a[0]不是等价的,a[0],&a[0][0]也不是等价的,这是为什么呢,那是在sizeof中,二维数组名a和一维数组名a[0]并没有退化。a表示整个二维数组(是一个标量),a[0]也表示整个二维数组的第一行(一个一维数组,也是标量)。所以可以得出结论退化后它们是等价的。

Sizeof()传入标量会计算该类存储空间的大小,传入指针自然是计算指针这个本质上是标量的空间的大小,而不是计算其指向地址空间的大小,传入字面值也计算其对应类型的大小。如sizeof(5);sizeof(&a);等。

 

下面援引<<c专家编程>>上一段话:

“数组名被改写成一个指针参数”的规则并不是递归定义的,数组的数组会被改写成“数组的指针”,而不是指针的指针。

实参

 

匹配形参(指针)

 

数组的数组

Char c[5][6]

Char (*c)[6]

数组指针

指针数组

Char *c[10]

Char **c

指针的指针

数组指针

Char (*c)[10]

Char (*c)[10]

不变

指针的指针

Char **c

Char **c

不变

 

从表中可知:当传数组的数组名c时,其实是传了一个 char (*c)[6]这样的指针,故此时c其实等价于&c[0],是二维数组首行的首地址(注意与首元素首地址含义是不一样的)(同理c+1是第二行首地址)。

注:看数据具体类型还可以在gdb下,其次用g++编译,然后故意复制给错误的对象,如全部赋值给整型,报错中会有类型说明。

下面我们来看看二维数组都有哪些声明方法:先看一个程序

//源码:

#include

 

void OutByIndexAll(int p[3][4])

{

     printf("the first way to OutPut a By IndexAll:\n");

     int i = 0;

     int j = 0;

     for (i;i < 3;i++)

     {

          printf("Row %d: ",i+1);

          for (j = 0;j < 4;j++)

          printf("%d ",p[i][j]);

          printf("\n");

     }

   printf("\n");

 

 

}

 

void OutByIndexPart(int p[][4],int num)

{

     printf("the second way to OutPut a By IndexPart:\n");

     int i = 0;

     int j = 0;

     for (i;i < num;i++)

     {

          printf("Row %d: ",i+1);

          for (j = 0;j < 4;j++)

          printf("%d ",p[i][j]);

          printf("\n");

     }

   printf("\n");

 

 

 

}

 

void OutByPToATwo(int (*p)[4],int num)

{

  printf("the second way to OutPut a By pointer to Array:\n");

  int count = 1;

  while(num--)

  {

          int j = 0;

          printf("Row %d: ",count++);

          for (j = 0;j < 4;j++)

          printf("%d ",*(*p+j));

          printf("\n");

      p++;

  }

   printf("\n");

}

 

 

void OutByPToAOne(int (*p)[4],int num)

{

     printf("the first way to OutPut a By pointer to Array:\n");

     int i = 0;

     int j = 0;

     for (i;i < num;i++)

     {

          printf("Row %d: ",i+1);

          for (j = 0;j < 4;j++)

          printf("%d ",p[i][j]);

          printf("\n");

     }

   printf("\n");

 

}

 

void OutByPToInt(int *p,int num)

{

     printf("the way to OutPut a By pointer to Int:\n");

     int i = 0;

     for (i;i < num;i++)

     {

          printf("%d ",p[i]);

     }

     printf("\n");

}

 

void OutByPToIntForTest(int *p,int row,int col) //sure error

{

     printf("the way to OutPut a By pointer to Int For test:\n");

     int i = 0;

     int j = 0;

     for (i;i < row;i++)

     {

          printf("Row %d: ",i+1);

          for (j;j < col;j++)

          //printf("%d ",p[i][j]);//编译通不过

          printf("\n");

     }

     printf("\n");

}

 

 

void OutByPToP(int **p,int row,int col)

{

   printf("the way to OutPut a By pointer to pointer:\n");

     printf("----the first way to dealwith:----\n");

     int all = row*col;

     int i =0;

     for (i;i < all;i++)

     {

          printf("%d ",p[i]);

     }

     printf("\n----the second way to dealwith:----\n");

     i = 0;

     int j = 0;

     for (i;i < row;i++)

     {

          printf("Row %d: ",i+1);

          for (j;j < col;j++)

        printf("%d ",p[i][j]);

        printf("\n");

     }

     printf("\n");

}

 

int main()

{

    

     int (*p)[4];

     int *r;

     int **q;

     int i = 0;

     int a[3][4] = {

                             1,2,3,4,

                             5,6,7,8,

                             9,1,2,3

                          };

     printf("\n****OutPut a use array name as argument:****\n");

     OutByIndexAll(a);

     OutByIndexPart(a,3);

     OutByPToAOne(a,3);

     OutByPToATwo(a,3);

     OutByPToInt(a,12);

     OutByPToInt(a[0],12);

     //OutByPToP(a,3,4);//

     printf("a = 0x%x   a+1 = 0x%x\n",a,a+1);

     printf("\n****OutPut a use pointer to array as argument:****\n");

     p = a;

     printf("p = 0x%x   p+1 = 0x%x\n",p,p+1);

     OutByIndexAll(p);

     OutByIndexPart(p,3);

     OutByPToAOne(p,3);

     OutByPToATwo(p,3);

     OutByPToInt(p,12);

     OutByPToInt(p[0],12);

     //OutByPToP(p,3,4);//函数前半部分正常后半部分不行,段错误

    

     printf("\n****OutPut a use pointer to Int as argument:****\n");

     r = a;

     printf("r = 0x%x   r+1 = 0x%x\n",r,r+1);

     OutByIndexAll(r);

     OutByIndexPart(r,3);

     OutByPToAOne(r,3);

     OutByPToATwo(r,3);

     OutByPToInt(r,12);

     //OutByPToInt(r[0],12);//r[0] in this is a value not a address!

     //OutByPToP(r,3,4);//同上

     //OutByPToIntForTest(r,3,4);//函数本身错误

     printf("\n****OutPut a use pointer to Pointer as argument:****\n");

     q = a;

     printf("q = 0x%x   q+1 = 0x%x\n",q,q+1);

     OutByIndexAll(q);

     OutByIndexPart(q,3);

     OutByPToAOne(q,3);

     OutByPToATwo(q,3);

     OutByPToInt(q,12);

     //OutByPToInt(q[0],12);//as above r[0]

     //OutByPToP(q,3,4);//同上

    

 

     return 1;

}

输出:

 

****OutPut a use array name as argument:****

the first way to OutPut a By IndexAll:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By IndexPart:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the first way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the way to OutPut a By pointer to Int:

1 2 3 4 5 6 7 8 9 1 2 3

the way to OutPut a By pointer to Int:

1 2 3 4 5 6 7 8 9 1 2 3

a = 0xbff88280   a+1 = 0xbff88290

 

****OutPut a use pointer to array as argument:****

p = 0xbff88280   p+1 = 0xbff88290

the first way to OutPut a By IndexAll:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By IndexPart:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the first way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the way to OutPut a By pointer to Int:

1 2 3 4 5 6 7 8 9 1 2 3

the way to OutPut a By pointer to Int:

1 2 3 4 5 6 7 8 9 1 2 3

 

****OutPut a use pointer to Int as argument:****

r = 0xbff88280   r+1 = 0xbff88284

the first way to OutPut a By IndexAll:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By IndexPart:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the first way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the way to OutPut a By pointer to Int:

1 2 3 4 5 6 7 8 9 1 2 3

 

****OutPut a use pointer to Pointer as argument:****

q = 0xbff88280   q+1 = 0xbff88284

the first way to OutPut a By IndexAll:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By IndexPart:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the first way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the second way to OutPut a By pointer to Array:

Row 1: 1 2 3 4

Row 2: 5 6 7 8

Row 3: 9 1 2 3

 

the way to OutPut a By pointer to Int:

1 2 3 4 5 6 7 8 9 1 2 3

 

printf("a = 0x%x   a+1 = 0x%x\n",a,a+1);

输出:a = 0xbff88280   a+1 = 0xbff88290

很容易理解a+1=a+4*4(一行)= 0xbff88280+16=0xbff88290

p = a;

printf("p = 0x%x   p+1 = 0x%x\n",p,p+1);

输出:p = 0xbff88280   p+1 = 0xbff88290

很容易理解:p的类型为int (*)[4], p=a没有隐式类型转化。p 指向数组首行首地址(以行为单位)。

r = a;

printf("r = 0x%x   r+1 = 0x%x\n",r,r+1);

输出:r = 0xbff88280   r+1 = 0xbff88284

明显r只是指向首元素的首地址(以单个元素为单位)。之所以其它调用成功是因为函数接受形参时也会发生默认形参转换。但此时r[0]表示a[0][0]=1;调用函数是转化是成功了,但检测到这不是系统授权的地址,不可使用,出现段错误。

q = a;

printf("q = 0x%x   q+1 = 0x%x\n",q,q+1);

输出:q = 0xbff88280   q+1 = 0xbff88284

和r情形一样,此时q指向数组首元素首地址,部分函数调用成功也是隐士类型转换缘故。q[0]等同于r[0]。(同理q[1] = r[1] 类型不同而已)

OutByPToP()函数为何之前的一半会正确呢,那是因为q指向的是数组首元素的首地址,而那么*q = a[0][0],*(q+1)= a[0][1]……

    那么后一半为何会错呢,这是有原因的,这是因为p它本身是个指针可以指向其他内存空间(即p中存储的必须是地址),另外,p中存放的地址所指向的内存空间还必须存放地址,这样对p的二级解引用才是合法的。

    怎奈此处发生了隐式的强制类型转换,实际赋予q的地址空间中存的不是地址,故二级解引用出现段错误,如:**q ó*(a[0][0])=*0x00000001,这个过程需要访问内存空间,是需要通过审核,系统说:“等等,我手头的内存分配表并没有查到该地址,你在试图访问非法内存,这是不允许的,年轻人可别想干坏事,我会严格执法,也别想贿赂我哦!”。

    我们需要重申一点,我们可以正常使用q的一级解引用,就像使用某个一级指针一样。

注:如果你不怕晦涩死人,你同样可以把二级指针当一级指针使用,只是你永远不需要使用它的二级解引用操作。

 

    从上面的讨论我们貌似可以得出一个结论,我们不可以用二维指针来像矩阵那样操作二维数组(如:int **p = a;p[i][j],或**p++)。 事实并非如此,如果二维数组向下面这样声明:

int *a[4];(或者char *a[4])

二维指针就会有用武之地了。

下面用一个简单的程序演示一下:

源码:

#include

 

 

int main()

{

     char *p1 = "Hello";

     char *p2 = "world";

     char *p3 = "people";

     char *a[3]={p1,p2,p3};

     char **p = a;

     int i = 0;

     int j = 0;

     while (i < 3)

     {

          j = 0;

          while (p[i][j] != '\0')

          {

              printf("%c",p[i][j]);

              j++;

          }

          printf(" ");

          i++;

     }

   printf("\n");

 

   return 1;

}

 至此告一段落!下面一节我们将讨论文件链接属性!

 

 

   

 

 

 


    


    
    
   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值