【问题标题】:pointers declaration fundamentals (assignment of multidimensional arrays pointer)指针声明基础(多维数组指针赋值)
【发布时间】:2016-04-29 17:43:47
【问题描述】:

我在函数内部遇到了这种构造(e 是传递给函数的参数):

short (*tt)[][2] = (short (*)[][2])(heater_ttbl_map[e]);

及其用途(其中 i 是 for 循环内的计数器):

(*tt)[i][0]

我想我得到了作业的第一部分:

short (*tt)[][2]

据我所知,tt 被声明为指向短裤数组的指针。 第二部分让我感到困惑,看起来像是某种演员,但我不确定我是否理解它的作用,特别是:(*)。它是如何工作的?

heater_ttbl_map 是这样声明的(其中pointer1和pointer2都是shorts的二维数组):

static void *heater_ttbl_map[2] = {(void*)pointer1, (void*)pointer2};

至于它的使用,我知道 tt 所指向的内容已被取消引用(以及它的数组 i 索引的第三个索引的内容,很短),但为什么要这样写:

(*tt)[i][0]

不是这样的:

*tt[i][0]

是不是因为 tt 本身不是数组而是指向数组的指针?

【问题讨论】:

标签: c++ c arrays pointers


【解决方案1】:

由于运算符优先级([] 优先于* 运算符),两个语句存在差异-

(*tt)[i][0]

在这里,您可以访问指针tt 指向的数组的索引[i][0] 处的元素。

然而,在这个 -

*tt[i][0]    

首先访问索引[i][0]可能是二维指针数组)处的元素,然后取消引用。

交替使用它们可能会导致访问或取消引用未经授权的内存位置并导致未定义的行为。

【讨论】:

  • 谢谢@ameyCU!所以我对其用法的直觉或多或少是正确的。这解决了第二个问题,但您对第一个更难的部分(声明)有什么提示吗?
  • @zakkos 第一部分 - (short (*)[][2])(heater_ttbl_map[e]); 。在这个sort (*)[][2] 应该是一个强制转换,因为heater_ttbl_map 是一个数组,heater_ttbl_map[e] 是一个二维数组(正如你在声明中指出的那样)。所以一定是上面语句中用到的case,然后tt指向这个二维数组。
【解决方案2】:

正如ameyCU 所解释的,[] 下标运算符的优先级高于一元运算符*,因此表达式*a[i]解析*(a[i]); IOW,您正在索引a 并取消引用结果。

如果aT 的数组(或指向T 的指针;更多内容见下文),则此方法有效。但是,如果a 是一个指向T 的数组 的指针,那将无法满足您的要求。这可能是最好的视觉解释。

假设声明:

int arr[3] = { 0, 1, 2 };
int (*parr)[3] = &arr;    // type of &arr is int (*)[3], not int **

这是内存中的情况(有点;地址是凭空提取的):

Address   Item   Memory cell
-------   ----   -----------
                 +---+
0x8000    arr:   | 0 |  <--------+
                 +---+           |
0x8004           | 1 |           |
                 +---+           |
0x8008           | 2 |           |
                 +---+           |
                  ...            |
                 +---+           |
0x8080    parr:  |   | ----------+
                 +---+
                  ...

所以你看到数组arr 及其三个元素,指针parr 指向arr。我们想通过指针parr访问arr的第二个元素(地址1处的值0x8004)。如果我们写*parr[1]会发生什么?

首先,记住表达式a[i]定义*(a + i);也就是说,给定一个指针值a1,从a 偏移i 元素不是字节)并取消引用结果.但是将i elementsa 偏移是什么意思呢?

指针算法是基于被指向类型的大小;如果p 是指向T 的指针,那么p+1 将为我提供下一个T 类型对象的位置。因此,如果p 指向地址为0x1000int 对象,那么p+1 将给我int 对象的地址在p - 0x1000 + sizeof (int) 之后。

那么,如果我们写parr[1],那会给我们带来什么?由于parr 指向一个三元素数组,如果intparr + 1 将为我们提供int 的下一个三元素数组的地址-0x8000 + sizeof (int [3]),或@987654365 @(假设 4 字节 int 类型)。

请记住,[] 的优先级高于一元 *,因此表达式 *parr[1] 将被解析为 *(parr[1]),其计算结果为 *(0x800c)

这不是我们想要的。要通过parr 访问arr[1],我们必须确保parr 已被取消引用之前通过使用括号将* 运算符显式分组来应用下标操作:(*parr)[1]*parr 计算结果为 0x8000,其类型为“int 的三元素数组”;然后我们访问该数组的第二个元素(0x8000 + sizeof (int)0x8004)以获得所需的值。

现在,让我们看一下——如果a[i] 等价于*(a+i),那么a[0] 就等价于*a。这意味着我们可以将(*parr)[1] 写为(parr[0])[1],或者只是parr[0][1]。现在,您不想在这种情况下这样做,因为parr 只是指向一维数组的指针,而不是二维数组。但这就是二维数组索引的工作方式。给定一个像T a[M][N]; 这样的声明,在大多数情况下,表达式a 将“衰减”为T (*)[N]。如果我写了类似的东西

int arr[3][2] = {{1,2},{3,4},{5,6}};
int (*parr)[2] = arr; // don't need the & this time, since arr "decays" to type
                      // int (*)[2]

然后要访问arrparr 的元素,我需要做的就是写parr[i][j]parr[i] 隐式 取消引用 parr 指针。


  1. 这就是事情变得混乱的地方;数组不是指针,它们内部不存储任何指针。相反,数组表达式不是sizeof 或一元* 运算符的操作数,其类型从“T 的N 元素数组”转换为“指向T 的指针”,其值表达式是数组第一个元素的地址。这就是您可以在数组和指针对象上使用[] 运算符的原因。

    这也是为什么我们在代码sn-p中使用&amp;操作符来获取arr的地址;如果它不是 `&` 运算符的操作数,则表达式“衰减”从类型“int”的三元素数组到“指向int 的指针”

【讨论】:

  • 抱歉回复晚了,我正试图解决所有这些问题!我可能仍然对铸造部分有疑问,总的来说,为什么作者决定使用这个非常复杂的网络,请原谅俗语,“指向/代表其他事物的事物”,而不是直接找到源头,因为在这个在这种情况下,它是已知的并且总是一个二维的短裤数组。现在,既然我们从heater_ttbl_map得到的确实是一个二维数组,那为什么还要再把它转换成一个二维数组呢?
  • @zakkos:没看到原代码,只能猜测。转换为void * 并再次返回通常意味着您正在通过某种“通用”接口(如qsortpthread_create 函数)调用此代码。
  • 是的,你是对的,不知道完整的代码很难猜到。仅供参考,这取自一个名为 Marlin 的流行 3D 打印机开源固件。回到话题,我注意到heater_ttbl_map 返回的不是数组而是指向二维数组的指针,现在我看到需要取消引用它,但我仍然不明白为什么强制转换是,如果它确实是必要的整个工作。取消引用还不够吗?或者需要强制转换可能是因为 heater_ttbl_map 是一个指针数组,其类型为 void 而不是“类型”short[][2]
猜你喜欢
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多