数组a和& a的起始地址

时间:2013-05-01 03:25:27

标签: c pointers

在以下两行中,

char a[5]={1,2,3,4,5};
char *ptr=(char *)(&a+1);
printf("%d",*(ptr-1));

这在屏幕上打印5个。当使用a代替& a时,

char a[5]={1,2,3,4,5};
char *ptr=(char *)(a+1);
printf("%d",*(ptr-1));

这打印1

a和& a都是数组的起始地址。所以为什么会出现这种差异?

另外      char * ptr =& a + 1;

显示警告。

5 个答案:

答案 0 :(得分:12)

数组不是指针!请阅读section 6 of the comp.lang.c FAQ以获取更多信息。

让我们先来看看你的第二个案例,因为它更“正常”,而且是惯用的。逐行:

  1. 您声明一个包含5个a元素的数组char
  2. 数组的名称(a)衰变为指向此上下文中第一个元素的指针。您向其添加1并将结果分配给ptrptr指向2。不需要演员,但你有一个。
  3. 您从1中减去ptr,然后取消引用并打印 - 因此您获得了1
  4. 现在,让我们再次逐行解决第一种情况:

    1. 您声明一个包含5个a元素的数组char
    2. 您获取a的地址,产生char (*)[5]类型指针。然后将1添加到此指针 - 由于指针算术,这个新指针会在内存中5后面的字节处过去。然后进行类型转换(必需,此时)并将此值分配给ptr
    3. 1中减去ptr,然后重新引用并打印。 ptr是一个char *,因此这个减法只是将指针从“a末尾的一个”移回一,指向a的最后一个元素。因此,您获得了5
    4. 最后,char *ptr=&a+1;给出警告的原因是因为C要求指针类型之间的转换具有显式强制转换。如上所述,&a的类型为char (*)[5]不是 char *,因此要将该值分配给char *变量,您需要显性演员。

答案 1 :(得分:10)

由于你似乎对它很陌生,让我用简单的语言向你解释,而不是去做严格的解释。

你知道,对于你上面的程序,a&a将具有相同的数值,我相信这就是你的整个混乱所在。你可能想知道如果它们相同,则在两种情况下,a后面的下一个地址将通过指针算法进行:

(&a+1) and (a+1)

但事实并非如此!! 数组的基地(此处a)和数组的地址不一样! a&a可能在数字上相同,但它们不是相同的类型a的类型为char*,而&a的类型为char (*)[5],即&a是指向(地址)和大小为5的数组的指针。但是a如您所知,是数组第一个元素的地址。在数字上,它们与使用下面的 ^ 从插图中看到的相同。 / p>

但是当你递增这两个指针/地址时,即(a+1)(&a+1),算术完全不同。而在第一种情况下,它会“跳转”到下一个元素的地址。数组,在后一种情况下,跳过5个元素,就像5个元素的数组的大小一样!。现在呢?

  1 2 3 4 5  
  ^               // ^ stands at &a

  1 2 3 4 5
           ^     // ^ stands at (&a+1)

  1 2 3 4 5
  ^              //^ stands at a

  1 2 3 4 5
    ^            // ^ stands at (a+1)

以下将给出关于未指定数组绑定的错误,因为未明确指定大小如下所示意味着当遇到类似(& a + 1)之类的程序时,程序将不知道要“跳转”到多少元素。

char a[]={1,2,3,4,5};
char *ptr=(char *)(&a+1);  //(&a+1) gives error as array size not specified.

现在到了将指针/地​​址递减为(ptr-1)的部分。在第一种情况下,在你来到减量部分之前,你应该知道它上面的语句中会发生什么char*

char *ptr=(char *)(&a+1);

这里发生的是,您“剥离”type (&a+1) char (*)[5]类型char*,现在将其转换为类型为a的{​​{1}} printf()的那个,即数组的基地址。(再次注意数组的基地址和数组的地址之间的差异。)之后上面语句中的强制转换和赋值,后跟ptr中的递减,5现在给出了数组最后一个元素后面的内存位置,即 1 2 3 4 5 ^ // ^ stands at location of 5, so *ptr gives 5

ptr

因此,当您将指针*(ptr-1)递减为5后取消引用时,它会按预期打印1的值。

现在最后,将其与第二种情况进行对比,其中a被打印出来。看看我使用符号 ^ 给出的插图。当您将a+1增加为2时,它指向数组的第二个元素,即ptr,并且您已将此地址分配给ptr。因此,当您减少(ptr-1)时{1}}它为1,它跳回一个元素,现在指向数组的第一个元素,即ptr。因此,在第二种情况下取​​消引用1会给 1 2 3 4 5 ^ // ^ stands at address of 1, so *ptr gives 1 }。

{{1}}

希望这一切都清楚。

答案 2 :(得分:2)

不同之处在于您获得的指针类型:

  • 数组名a本身表示指向数组初始元素的指针。当以这种方式解释时,例如,在表达式a+1中,指针被视为指向单个字符。
  • 另一方面,当您使用&a时,指针指向一个包含五个字符的数组。

向指针添加整数时,指针移动的字节数由指针指向的对象指针的类型决定。如果指针指向char,则添加N会使指针前进N个字节。如果指针指向一个包含五个char的数组,则添加N会使指针前进5*N个字节。

这正是你得到的差异:你的第一个例子将指针推进到一个超过数组末尾的元素(这是合法的),然后将其移回最后一个元素。另一方面,您的第二个示例将指针前进到第二个元素,然后将其移回指向数组的初始元素。

答案 3 :(得分:0)

数组“衰减”成指向第一个元素的指针。因此,获取a的地址会给出一个指向5个字符数组的指针,就像声明char[][5]一样。并且递增此指针前进到char[][5]数组的下一个元素 - 一次是5个字符。这与递增从char[5]数组衰减的指针(即一次只有一个字符)不同。

答案 4 :(得分:0)

你遇到的是指针运算的微妙之处。

编译器将“a”视为指向char的指针 - 一个大小为1字节的实体。向此处添加1会产生一个指针,该指针会增加实体的大小(即1)。

编译器将“& a”视为指向chars数组的指针 - 一个大小为5个字节的实体。向此处添加1会产生一个指针,该指针会增加实体的大小(即5)。

这是指针算法的工作原理。将一个指针添加到指针会使其增加一个指针类型的大小。

当然,有趣的是,当评估“a”或“& a”的价值时,在解除引用时,它们都会评估到同一地址。这就是你看到你所做的价值的原因。