【问题标题】:Pointer dereference array index指针解引用数组索引
【发布时间】:2020-05-07 22:22:02
【问题描述】:

有这个:

#include <stdio.h>
#include <stdlib.h>

struct Test { char c; } foo;

int main (void) {

   struct Test **ar;
   ar=malloc(16);
   *(ar+1) = &foo;
   ar[1]->c = 'c'; //this work
  (*(*ar+1)).c = 'c'; //this does't work

   return 0;
}
        //(**(ar+1)).c='c'; --> first case

为什么上面的方法只适用于带有数组条目的变体而不是指针解引用?

struct Test { char c; } foo;

int main (void) {

   struct Test **ar;
   ar=malloc(16);
   *ar=malloc(0);
   *(ar+1) = &foo;
   //(**(ar+1)).c='c';
   (*(*ar+1)).c='c'; // NOW IT WORKS --> second case
   printf("%c\n", (*(*ar+1)).c); //prints 'c'

   return 0;
}

现在甚至分配了 0 个字节,这没关系,因为我只想要操作系统提供的地址以便初始化第一个元素

问题:指针算法在这两种情况下是如何工作的? 据我了解:

1) 首先为了得到struct Test的左值,指针直接从ar指向的地址指向**ar的左值-sizeof(struct Test**)

2) 在第二种情况下,指针确实初始化了第一个成员ar[0],因此它从*ar 开始,并通过*ar - sizeof(struct Test*) 到达左值。

但是两个指针的大小相同sizeof(struct Test**) == sizeof(struct Test*),因此在算术上应该没有区别,或者我错过了什么?

【问题讨论】:

  • C Operator Precedence'*' 的优先级高于'+' 那么(*(*ar+1)).c 发生了什么?
  • 使用索引符号“this work”是ar[1][0].c = 'c';,“this doesn't work”是ar[0][1].c = 'c',这不起作用,因为您从未为ar[0]设置值。在第二种情况下,您会导致未定义的行为,但有时会表现为似乎在做您期望的事情

标签: c pointers initialization undefined-behavior pointer-arithmetic


【解决方案1】:
struct Test **ar;
ar=malloc(16);
...
(*(*ar+1)).c = 'c'; //this does't work

当然可以。正如我在评论中指出的那样,* 的优先级高于+C Operator Precedence。那么(*(*ar+1)).c 发生了什么?看:

(*ar+1)

相当于:

(ar[0] + 1)

因为artypepointer-to-pointer-tostruct Test*arar[0] 是类型指针-到 struct Test。然后你添加 + 1sizeof (struct Test*) 添加到第一个指针 ar 这就是你想要的。

为什么会这样?运算符优先级:

   *ar              /* dereference ar** leaving pointer to struct Test */

  (*ar + 1)         /* advance to next pointer - applied before next dereference */

 *(*ar + 1)         /* dereference again leaving struct Test assigned to 2nd pointer */

(*(*ar + 1)).c      /* reference member 'c' of above */

在玩多级间接时,可读性至关重要。使用索引符号会有很大帮助。比起(*(*ar + 1)).c = 'c';,写起来干净多了:

(*ar)[1].c = 'c';

这清楚地表明您首先取消引用 ar,然后再应用 1 的偏移量并再次取消引用([..] 提供取消引用,就像 '*' 所做的那样)到达分配的第二个点。

【讨论】:

  • 我理解上面的,有道理,但是这里不理解指针运算。说如果我这样做*ar=malloc(0);那么这将工作(*(*ar+1)).c = 'c',只是因为我已经分配了第一个元素。所以它按大小 struct Test* 排列 1 个索引。但是如果我不初始化第一个元素,而是直接转到左值-> (**(ar+1)).c='c',它的大小为 struct 'Test**',但两个指针(第一个简单指针,第二个指向指针的指针)大小相同8 个字节。那么为什么要有所作为呢?
  • 为什么我在(*(*ar+1))中添加struct Test by +1?我虽然,通过取消引用*ar 现在是pointer-to-Test(如你所说),所以通过在(*(*ar+1)) 中添加+1 我会添加类型pointer-toTeststruct Test * 而不仅仅是@987654355 @。这个(*ar+1) 是指针中的算术,它们指向的对象
  • 您使用:) 语法将我们俩都包裹在轴上。是的,这行得通。 *arpointer to struct Test*ar + 1advance to next pointer; *(*ar + 1)struct Test assigned to 2nd pointer 最后是 (*(ar + 1)).c 引用 member 'c' of above
【解决方案2】:

对于初学者,您应该正确地指定分配内存的大小

   ar = malloc( 2 * sizeof( struct Test * ) );

此声明

*(ar+1) = &foo;

将分配的指针数组的第二个元素设置为全局变量 foo 的地址。

一样
ar[1] = &foo;

这个表达式

*ar

相当于表达式

ar[0]

给出分配的指针数组的第一个元素。它没有被初始化。结果这个表达式

*ar+1

ar[0] + 1

调用未定义的行为(将 1 添加到未初始化且具有不确定值的内容)。

看来你的意思

(**(ar+1)).c = 'c';

这就是表达式

*( ar + 1 )

给出动态分配的指针数组的第二个元素。取消引用它,您将获得对象foo 的地址。在第二次获得对象 foo 本身的左值时取消引用。

注意那个表达方式

ar[1]

等价于

*( ar + 1 )

你可以从这个有效的声明中看到

ar[1]->c = 'c'

上面的表达式产生一个指针。所以如果你想使用操作符,你需要取消引用它。

**( ar + 1 )

【讨论】:

  • 为什么我需要初始化第一个指针*ar,才能使用初始化为(ar+1)=&amp;foo的seonc?
  • @Herdsman 您正在访问表达式 *ar + 1 中的第一个未初始化的指针。它与 *( ar + 1 ) 不同。
  • 所以我没有初始化第一个元素,以便通过 (**(ar+1)) 到达第二个元素,但是如果我想到达第二个“throught”第一个元素,我需要先初始化吗?因为如果是这样,那么我会工作
  • @Herdsman 请再读一读我的答案。我已经更新了。
  • 我理解上面的,有道理,但是这里不理解指针运算。说如果我做*ar=malloc(8); 那么这将工作(*(*ar+1)).c = 'c',只是因为我已经分配了第一个元素。所以它按大小为 1 个索引 struct Test*。但是如果我不初始化第一个元素,而是直接转到左值-> (**(ar+1)).c='c',它的大小是struct Test**,但是两个指针(第一个简单指针,第二个指向指针)的大小相同,均为 8 字节。那么为什么要有所作为呢?
【解决方案3】:

您想取消引用以获取ar[i],让我们这样做:

(*(ar+1))->c = 'c';

【讨论】:

  • 您的编辑*(ar+1) = &amp;foo; 然后(*(*ar+1)).c = 'c';。它没有任何意义。 *arr+1*(arr+1) 不同
猜你喜欢
  • 2016-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-23
  • 1970-01-01
  • 1970-01-01
  • 2010-11-29
  • 2023-02-08
相关资源
最近更新 更多