***************************************************
更多精彩,欢迎进入:http://shop115376623.taobao.com
***************************************************
相信不少的C语言初学者都知道,数组名相当于指针,指向数组的首地址,而函数名相当于函数指针,指向函数的入口地址。
现在有这样一个问题,如果对数组名取地址,那得到的会是什么呢?很多人立刻会想到:给指针取地址,就是指针的指针,
即二级指针嘛!当然这样的结论是错误的,不然这篇笔记也就没有意义了。
下面我们来逐步分析,下面是一段验证这个问题的代码
- #include<stdio.h>
- int main()
- {
- int a[10];
- printf("a:/t%p/n", a);
- printf("&a:/t%p/n", &a);
- printf("a+1:/t%p/n", a+1);
- printf("&a+1:/t%p/n", &a+1);
- return 0;
- }
大家可以编译运行一下,我的输出的结果是:
- /*
- a: 0012FF20
- &a: 0012FF20
- a+1: 0012FF24
- &a+1: 0012FF48
- */
a和&a指向的是同一块地址,但他们+1后的效果不同,a+1是一个元素的内存大小(增加4),而&a+1增加的是整个数组的内存
大小(增加40)。既a和&a的指向和&a[0]是相同的,但性质不同!
读到这里,有很多朋友已经明白其中的机制了,如果还是有些模糊,请继续往下看
- int main()
- {
- int a[10];
- printf("%d/n",sizeof(a));
- return 0;
- }
这段代码会输出整个数组的内存大小,而不是首元素的大小,由此我们是否联系到,sizeof(a)这里的a和
&a有些相同之处呢?! 是的,没错,&a取都得是整个数组的地址!既数组名取地址等价于对数组取地址。
好了,让我们总结一下,如果你还是不太理解,不用担心,下面的概念很清晰
其实a和 &a结果都是数组的首地址,但他们的类型是不一样。
a表示&a[0],也即对数组首元素取地址,a+1表示首地址+sizeof(元素类型)。
&a虽然值为数组首元素地址,但类型为:类型 (*)[数组元素个数],所以&a+1大小为:首地址+sizeof(a)。
还有这篇文章最初提到的指针的指针的那个错误结论,其实即使不懂上述内容,也应该判断出结论是错误的,大家应该在了解数组名
即是数组的首地址的同时,也要知道,数组名仅仅是“相当”于指针,而并非真的是指针,数组名是只是个常量(一个值为数组首元素
地址的常量),所以不能进行++或者--运算。而常量更是无法取地址的,而之所以有&a,其实这里的a的意义早已经不是当初那个数组
名了,它此时代表了整个数组。
申明:本文系原创,转载时请注明出处,本人保留追究责任的权利。 原文地址:http://hi.baidu.com/surfmygod/blog/item/53d44914cdb8a5d6a7ef3f13.html 本文适用于机器为32位,编译器为VC6.0。 先来看下面一个例子:main() { int a[5]={0x11121314,0x21222324,0x31323334,0x41424344,0x51525354}; int *ptr1=(int *)(&a+1); int *ptr2=(int *)(a+1); printf("%x,%x",ptr1[-1],*ptr2); getch(); } 打印输出的结果是多少?第4行&a是不是写错了?数组名怎么还取地址呢? 你可以试着在软件中编译运行一下,结果是51525354,21222324,没错,就是它了。你可以试着写以下程序
再在计算机中编译运行下:
main() { int a[5]={0x11121314,0x21222324,0x31323334,0x41424344,0x51525354}; printf("%p\n%p\n",a,&a); printf("%d\n%d\n",sizeof(a),sizeof(&a)); printf("%p\n%p",&a+1,a+1); getch(); } 你可以看到前两行a和&a打印的结果是一样的(比如0012FF6C),后两行的结果是都是20,而&a+1的结果是0012FF6C, 即a的起始地址后移20字节。看到这里对于a和&a的区别是不是有些眉目了?不错,a和&a的值虽然一样,但是前者表示 的是数组a的首地址,进阶为数组元素长度,而后者也是表示数组a的首地址(或许有些人不同意我这个观点,对地址 取地址应该是指向指针的指针呀。不过你注意了没有,a和&a值是一样的,试想如果这里表示的是指向指针的指针, 那么a的地址和指向a的指针的地址可能一样么? 两个指针都指向数组首地址,怎么会是指向指针的指针?还有一点需要强调,对一个指针进行sizeof运算,32位系统 下应该得到4,而对数组名进行sizeof运算得到的结果却是20,这说明了什么?很显然,数组名虽然能够拿出来代表 一个地址,但是它和指针还是有区别的,对它进行sizeof运算也不会得到指针的大小。不信?你可以试试sizeof(&a[0]) 和sizeof(a),看看结果是不是相同), 但是是将整个数组作为一个对象来看待的,进阶为整个数组长度。也就是说,a和&a虽然值相同,但是a是将所有数组 元素看成一个个不同的个体来对待的,而&a是将整个数组作为一个整体来对待的。这里要强调的是,a+1和&a[1]其实 是一样的,都是代表a数组向后偏移一个元素的地址,你可以再用sizeof关键字打印一下两个的值。但是a和&a[0]的 意义相同么?若打印地址的话,两者确实是一样的,但是你也用sizeof打印下两者,一样么?再试试a+0呢?很显然, a和a+0是有区别的,但是a+0和&a[0]是含义相同的。所以,你可以说a+i和&a[i]含义相同,但是绝不能说a和&a[0] 或a+0含义相同。所以&a+1后移20位为&a+sizeof(&a),而a+1却是a+sizeof(a[i])。 (《C语言深度解剖》认为在32为系统下sizeof(&a)的值仍为4,我觉得是不对的。他觉得是VC6.0错了,其实不止VC, 其他很多编译器得到的结果也不会是4,这说明a和&a在C中本来就不是作为指针来用的,只是具有指针的部分性质而已。)
ptr1是将&a+1强转为int类型指针,此时打印ptr1[-1]即将ptr指针前移sizeof(int)个字节,故打印结果是5。 这样一来后面的也明了了,a+1的值为在数组a的首地址上后移sizeof(int)个字节,然后强转为int类型指针 (其实强转并没有改变什么,原来类型即为整型指针,可以去掉强转),最后输出结果为21222324。
这里要考虑数据在计算机中的存储模式:大端模式和小端模式。解释一下: 大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。 小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。 在大端模式下,a在计算机中存储如下(从低地址到高地址,一个十六进制数代表内存的一个字节,下同): 0x11 0x12 0x13 0x14 0x21 0x22 0x23 0x24 0x31 0x32 0x33 0x34 0x41 0x42 0x43 0x44 0x51 0x52 0x53 0x54 在小端模式下,a在计算机中存储如下: 0x14 0x13 0x12 0x11 0x24 0x23 0x22 0x21 0x34 0x33 0x32 0x31 0x44 0x43 0x42 0x41 0x54 0x53 0x52 0x51 (int)a表示将a的首地址强转为整型数据(若原来是0012FF6C,转换后仍为0012FF6C,但是这时已经不是表示地址而是一个
十六进制整型数了),这时+1代表整型数(int)a加1(变为0012FF6D),再把结果强转为整形指针,故指向的数据地址为0012FF6D,
即上述存储去的第二个字节开始处。此时输出*ptr2,即输出a数组存储区中第二到第五字节组成的一个整型数,若为大端模式则输出
12131421,若为小端模式则输出24111213。
补充说明下,在vc6.0上,上面sizeof(&a)才是20,而在GCC ,以及之后的vc版本(如vs2005及之后版本)则是将&a完全视作一个指针,
sizeof(&a)为4 (32位机器)
int *p1[3], (*p2)[2], ** p, a[3][2];a) p1=a;是否语法正确? b) p2=a;呢? ? 为什么?请详述原因。 c) p=a; 是否正确???可不可以认为二维数组名是一个指向指针的指针变量?
示例程序:
#if 1
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[] = {1,2,3,4,5};
int *p1 = (int *)(a+1);
int *p2 = (int *)(&a+1);
int *p3 = (int *)((int)a + 1);
int *p4 = a;
printf("a is %p \n",a);
printf("&a is %p \n",&a);
printf("sizeof(a) = %d\n",sizeof(a));
printf("sizeof(&a) = %d\n",sizeof(&a));
printf("sizeof(p4) = %d \n",sizeof(p4));
printf("p1 = %p \n",p1);
printf("p2 = %p \n",p2);
printf("*p1 = %d\n",*p1);
printf("p2[-1] = %d\n",p2[-1]);
printf("*p3 = %d \n",*p3);
printf("*(p2-1) = %d \n",*(p2-1));
system("PAUSE");
return 0;
}
#endif
|