作者TobyH4cker (Toby (我要当好人))
看板C_and_CPP
标题Re: [问题] 阵列名称&指标常数问题
时间Fri Feb 3 22:42:53 2017
※ 引述《anoymouse (没有昵称)》之铭言:
: 关於"阵列名称的位址"跟"阵列第一个元素的位址"相同但值不同的问题
: &name=0x0000 &name[0]=0x0000
: name =0x0000 name[0]=1
: 在板上只看到LPH66大的回文,其实看不太懂
: 上网看大部分的解释大概是说:
: 如果有一个阵列int name[3]={1,2,3},
: &name[0]=0x0000
: &name[1]=0x0004
: &name[2]=0x0008
: 其阵列名称name是一个指向阵列第一个元素的位址的指标,也就是指标常数
: name=0x0000
: name+1=0x0004
: name+2=0x0008
: 如果阵列名称name碰到两个operator:sizeof() or &
: 变成&name就会是指到整个阵列的位址,所以&name+1就会变成0x000C
: 意思是说阵列名称(指标常数)其实并没有自身的位址的概念?
: 如果只是取阵列元素如name[0]那就会自动decay(网路上都用这个词)成指标 *(name+0)
: 如果是使用&name那只是纯粹得到整个阵列的位址? &name并不是指标常数的位址?
: 不知道这样问大家是否看的懂? 谢谢!
嗨嗨,先打个程式测测看就知道罗
#include <stdio.h>
int main()
{
int array[3] = {6, 8, 9};
printf("sizeof(int): %d (0x%X)\n", sizeof(int), sizeof(int));
printf("sizeof(array): %d (0x%X)\n", sizeof(array), sizeof(array));
printf("array: %p\n", array);
printf("&array: %p\n", array);
printf("&array[0]: %p\n", &array[0]);
printf("&array[1]: %p\n", &array[1]);
printf("&array[2]: %p\n", &array[2]);
printf("array + 0: %p\n", array + 0);
printf("array + 1: %p\n", array + 1);
printf("array + 2: %p\n", array + 2);
printf("&array + 0: %p\n", &array + 0);
printf("&array + 1: %p\n", &array + 1);
printf("&array + 2: %p\n", &array + 2);
printf("*(array + 1): %d\n", *(array + 1));
printf("*array + 1 : %d\n", *array + 1);
return 0;
}
以gcc-x86在Windows编译执行结果如下:
sizeof(int): 4 (0x4)
sizeof(array): 12 (0xC)
array: 0061FF24
&array: 0061FF24
&array[0]: 0061FF24
&array[1]: 0061FF28
&array[2]: 0061FF2C
array + 0: 0061FF24
array + 1: 0061FF28
array + 2: 0061FF2C
&array + 0: 0061FF24
&array + 1: 0061FF30
&array + 2: 0061FF3C
*(array + 1): 8
*array + 1 : 7
首先关於对阵列中每个元素取址这部分应该不会有太大困难,
取得的指标就是该元素的记忆体位址,
再来对阵列名称取址以及单纯阵列名称都表示阵列开头的位址,
而阵列的开头也就是第一个元素,
因此这三个位址是一样的没问题:
array: 0061FF24
&array: 0061FF24
&array[0]: 0061FF24
接着阵列名称 + 常数,这个就比较不好理解了,
你可以直觉把他从
array + n
转换成
&array[n]
不过要注意的是当有上下文的时候要注意优先序,
因为你想要做的是对指标做dereference,而不是先提领再运算,
所以要小心
int result = *(array + 1);
和
int result = *array + 1;
的差别,你也许会疑惑为什麽可以直接dereference阵列名称,
还记得他们都是阵列第一个元素的位址吗?
因此 *array 提领的结果就是 array[0] 的值了。
最後我想你最大的困难是在
&array + 1
上面我们发现某个指标加上常数偏移就会等於以该指标作为阵列的第常数个元素的位址,
听起来很饶口,但是我要你观察的是 array + n 的偏移量,
或是更一般性的说我们要观察 ptr + n 的偏移量意义,
藉由观察 array + 1 以及 array + 2 执行的结果:
array + 1: 0061FF28
array + 2: 0061FF2C
我们发现两个位址相减
0061FF2C - 0061FF28
= 4
由此可知每对这个阵列名称 + 1,就会得到往後4个bytes的位址,
这4个bytes是哪里来的?
上面程式一开始我就写了 sizeof(int),
而 int 正是这个阵列携带元素的型态,而每个元素的大小是4个bytes,
同理如果把 array + 1 和 array + 2 套回刚刚的公式
array + 1 = &array[1]
array + 2 = &array[2]
以元素在阵列中的位址来思考的话,
不难发现第三个元素的位址 &array[2] 比第二个元素的位址 &array[1]
多了4个bytes,也就是元素的大小。
到这边整理一下,
阵列名称 + 常数偏移
array + n
的位址 // 以下都把指标当作整数运算
= 阵列开头的位址 + n * (阵列元素的大小)
= &array[0] + n * sizeof(int) // 阵列的开头就是第一个元素
= &array + n * sizeof(array[0]) // 第一个元素型态是int
= array + n * sizeof(*array) // *array就是第一个元素
= ptr + n * sizeof(*ptr)
这样归纳,可以直接导出我要讲的结论:
&array + 1 的位址是多少呢?套用上面公式的话
其位址
= &array + 1 * sizeof(*&array) // 指标当作整数,此处非C语言
这边我们知道 &array 是指标对吧,
这个指标是一个指向「带有3个int的阵列」的指标,
我们把该指标命名为 ptr,其定义写成 int (*ptr)[3];
declare ptr as pointer to array 3 of int
因为这个指标指向的元素是一个「带有3个int的阵列」,
而又3个int的阵列的大小是3 * 4 = 12 (0xC),
故 ptr + 1 的位址就会是 ptr的位址 + 1 * 12,
再验证程式的结果:
&array + 0: 0061FF24
&array + 1: 0061FF30
&array + 2: 0061FF3C
该指标每多 + 1,位址就会 + 12 (0xC),没错。
到这边如果都能懂的话,
想一想,&array + 1 位址是不是就是 &array[3] 呢?
希望以上的说明能让大家多一种思考方式,也希望能帮助到各位。
提供一点小公式 // 此处都是C语言没问题
&array[0] = array + 0 = array = &array
elements of array: sizeof(array)/sizeof(*array)
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 39.9.233.174
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1486132977.A.B3A.html
1F:推 prismwu: 写真清楚 02/04 00:13
2F:推 cuteSquirrel: 推荐这篇文章 02/04 13:28
3F:推 s89227: 推一个 02/04 20:18
4F:推 MasTerNBHD: 推,满少看到 &变数+常数的 XD 02/04 22:20
5F:推 joj4211: 666666 02/06 00:37
6F:推 jerryh001: 第二个printf少一个& 02/06 12:14
7F:推 HowLeeHi: &array+1知道怎麽计算,但没有很详细去了解原因... 02/07 03:07
8F:→ HowLeeHi: 好像平常也比较少出现的样子!?? 02/07 03:07
9F:推 anoymouse: 谢谢! 03/08 17:12