引用 彻底说明二维数组数组名和指针的关系

#include <stdio.h>

void main()
{

      int a[2][3]={1,2,3,4,5,6};
      int b=0;
      int c=0;
//   
 b=*((int*)&a[1]+2); //OK
//   
 b=*(a[1]+2);//ok
//    b=*(*(a+1)+2);//ok
     
 b=*((int*)((int)&a[1]+2*sizeof(int)));//ok
      printf("%d\n",b);
//    
结果均是:6

//    c=*(&a[1]+2*sizeof(int));// c!=a[1][2]
//    printf("%d\n",c);
//    
结果:1245140(十六进制为:12FFD4)

//    c=*((int)&a[1]+2*sizeof(int));//error C2100: illegal indirection

//   
 c=*(&a[1]+2);//c!=a[1][2]
      printf("%d\n",c);
//    
结果:1245068(十六进制为:12FF8C)

}
结论:拿int a[2][3]={1,2,3,4,5,6}来说,&a其值是数组的首地址不是数组首元素的首地址(虽然它和首元素的首地址相同)&a的类型是int (*)[2][3]所以当&a+n,实际是按照&a(计算的过程中把&a认为是地址值,是个无符号整型变量啦,不再考虑它的类型啦)+n*sizeof(a),来得到的新地址(也是无符号整型变量)这个新地址肯定是数组a的界外,计算结果的类型还是   int (*)[2][3]其中sizeof(a)是代表数组占的内存大小(就是数组a所有元素占的总字节数)

a(作右值时)&a[0],他们值相同,类型也相同(或相当),都是二维数组a第一个元素a[0](它也是个数组,是个长度为3的一维数组)的首地址(就是一维数组的首地址),类型为int [][3]也即int (*)[3](一维数组的指针),所以a&a[0],&a[1](a第二个元素)的类型是相同(相当)的。所以当a+1&a[0]+1时,他们均代表着a(计算的过程中a是元素a[0]地址值,是个无符号整型变量啦,不再考虑它的类型啦。下同)&a[0](是地址值,是个无符号整型变量啦) +1*sizeof(a[0])后的地址值,结果就是a的第二个元素a[1](长度为3的一维数组)的首地址,而且它的类型还是int(*)[3],其中sizeof(a[0])是求数组a中第一个元素a[0]这个一维数组的占的内存大小,不能用sizeof(a)

a[0]&a[0][0],他们的值和类型都是相同的,都是二维数组a第一个元素中含的第一个元素的首地址,类型是int [3],也即是int *
类似的,a[1]&a[1][0],他们的值和类型都是相同的,都是二维数组a第二个元素中含的第一个元素的首地址,类型是int [3],也即是int *
所以 a[0],a[1],&a[0][0],&a[0][1],&a[0][2],&a[1][0],&a[1][1],&a[1][2]的类型相同,都是int *所以当a[0]+2&a[0][0]+2,他们均代表着二维数组a第一个元素中含的第一个元素的首地址+2*sizeof(a[0][0])后得到地址,也就是按照&a[0][0]) (二维数组a的第一个元素中含的第一个元素的首地址)+2*sizeof(int),因为数组是int类型的,就是元素a[0][2]的地址&a[0][2]

a[0][0],a[1][2]这样的的类型显然是int

通过上面的总结再次验证啦这样一条规定:数组名是其元素(如果数组是n维数组,那么这里的元素就是指每一个n-1维数组)的首地址,而不是本数组的首地址。例如:int a[2][3]={1,2,3,4,5,6}; (数组名)a不是数组a的首地址(数组a的首地址是&a),而是元素a[0](长度为3的一维数组)的首地址。(一维数组名)a[0]是数组a[0]的第一个元素a[0][0]的首地址,而不是数组a[0]的首地址(数组a[0]的首地址是a,或&a[0])

###############################################
###############################################
以下转自于C语言深度解剖》
另外有如下2个例子:
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}

对指针进行加操作,得到的是下一个元素(这里的元素是指针所指向的那种类型,可能是单个char类型,int类型,甚至是数组,结构体等)的地址,而不是原有地址值(地址值是无符号整型类型)直接加1。所以,一个类型为的指针的移动,以sizeof(T) 为移动单位。因此,对上题来说,是一个一维数组,数组中有个元素; ptr 是一个int 型的指针。
&a + 1: 
取数组的首地址,该地址的值加上sizeof(a) 的值,即&a + 5*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。
(int *)(&a+1): 
则是把上一步计算出来的地址(地址值),强制转换为int * 类型,赋值给ptr
*(a+1): a,&a 
的值是一样的,但意思不一样,是数组首元素的首地址,也就是a[0]的首地址,&a 是数组的首地址,a+1 是数组下一元素的首地址,即a[1]的首地址,&a+1 是下一个数组的首地址。所以输出2
*(ptr-1): 
因为ptr 是指向a[5]ptr的值实际上和&a[5]相等,显然a[5]在数组a里并不存在,只是可以这样理解,所以显然当前指针已经越过了数组的界限。并且ptr int * 类型,所以*(ptr-1) 是指向a[4] 
输出5

x86 系统下,其值为多少?
intmain()
{
int a[4]={1,2,3,4};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}
根据上面的讲解,&a+1 a+1 的区别已经清楚。
ptr1:将&a+1 的值强制转换成int*类型,赋值给int* 类型的变量ptr1ptr1 肯定指到数组a之后的,紧挨着数组a的一个占4字节(因为ptr指向int类型的数据)大小的内存的首地址。ptr1[-1]被解析成*(ptr1-1),即ptr1 往后退byte(1int类型数据的大小),就是指向数组元素a[3],所以其值为0x4(十六进制)
ptr2:按照上面的讲解,(int)a+1 的值是元素a[0]的第二个字节的地址。然后把这个地址强制转换成int*类型的值赋给ptr2,也就是说*ptr2 的值应该为元素a[0]的第二个字节开始的连续byte 的内容。这4个字节内存跨了a[0],a[1]两个元素,这连续byte 里到底存了什么东西呢?也就是说元素a[0],a[1]里面的值到底怎么存储的。这就涉及到系统的大小端模式了。既然不知道当前系统是什么模式,那就得想办法测试。,我们完全可以利用union 类型数据的特点:所有成员的起始地址一致来测试系统是什么模式
我们可以用下面这个函数来测试当前系统的模式。
int checkSystem( )
{
union check
{
int i;
char ch;
} c;
c.i = 1;
return (c.ch ==1);
}

如果当前系统为大端模式这个函数返回0;如果为小端模式,函数返回1
也就是说如果此函数的返回值为的话,*ptr2 的值为0x2000000
如果此函数的返回值为的话,*ptr2 的值为0x100

最后还要补充说明的一点:其详细内容可以参考:http://blog.csdn.net/rjzou2006/archive/2008/04/15/2292698.aspx
数组名不完全等于指针

数组什么时候会"退化"

下面是C99中原话:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.

上面这句话说的很清楚了数组在除了3种情况外其他时候都要"退化"成指向首元素的指针.
比如对 char s[10] = "china";
3中例外情况是:
(1) sizeof(s)
(2) &s;
(3) 
用来初始化s"china";

除了上述3种情况外,s都会退化成&s[0], 这就是数组变量的操作方式

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值