繁体   English   中英

创建一个指向二维数组的指针

[英]Create a pointer to two-dimensional array

我需要一个指向静态二维数组的指针。 这是怎么做的?

static uint8_t l_matrix[10][20];

void test(){
   uint8_t **matrix_ptr = l_matrix; //wrong idea 
}

我收到各种错误,例如:

  • 警告:从不兼容的指针类型赋值
  • 下标值既不是数组也不是指针
  • 错误:灵活数组成员的无效使用

在这里你想创建一个指向数组第一个元素的指针

uint8_t (*matrix_ptr)[20] = l_matrix;

使用 typedef,这看起来更干净

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

然后你可以再次享受生活:)

matrix_ptr[0][1] = ...;

当心 C 中的指针/数组世界,围绕这一点有很多困惑。


编辑

在此处查看其他一些答案,因为评论字段太短而无法在此处执行。 提出了多种替代方案,但没有显示它们的行为方式。 这是他们的做法

uint8_t (*matrix_ptr)[][20] = l_matrix;

如果您修复错误并在以下代码段中添加地址运算符&就像

uint8_t (*matrix_ptr)[][20] = &l_matrix;

然后那个创建一个指向不完整数组类型的元素的指针,该数组类型为 20 uint8_t。 因为指针指向一个数组数组,所以你必须用

(*matrix_ptr)[0][1] = ...;

而且因为它是指向不完整数组的指针,所以不能作为快捷方式

matrix_ptr[0][0][1] = ...;

因为索引需要知道元素类型的大小(索引意味着向指针添加一个整数,因此它不适用于不完整的类型)。 请注意,这仅适用于C ,因为T[]T[N]是兼容类型。 C++ 没有兼容类型的概念,因此它会拒绝该代码,因为T[]T[10]是不同的类型。


以下替代方法根本不起作用,因为数组的元素类型,当您将其视为一维数组时,不是uint8_t ,而是uint8_t[20]

uint8_t *matrix_ptr = l_matrix; // fail

以下是一个不错的选择

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

你访问它

(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now

它的好处是保留了外部维度的大小。 所以你可以在它上面应用 sizeof

sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20

还有另一个答案利用数组中的项目连续存储这一事实

uint8_t *matrix_ptr = l_matrix[0];

现在,这正式只允许您访问二维数组的第一个元素的元素。 即,以下条件成立

matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid

matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior

您会注意到它可能在10*20-1 ,但是如果您进行别名分析和其他积极优化,某些编译器可能会做出可能破坏该代码的假设。 话虽如此,我从未遇到过失败的编译器(但再说一次,我没有在实际代码中使用该技术),甚至 C FAQ 也包含该技术(并警告其 UB'ness ),如果你不能改变数组类型,这是拯救你的最后一个选择:)

完全理解这一点,您必须掌握以下概念:

数组不是指针!

首先(而且它已经被充分宣传了),数组不是指针 相反,在大多数用途中,它们“衰减”到它们的第一个元素的地址,该地址可以分配给一个指针:

int a[] = {1, 2, 3};

int *p = a; // p now points to a[0]

我假设它以这种方式工作,以便可以访问数组的内容而无需复制所有内容。 这只是数组类型的一种行为,并不意味着它们是同一回事。



多维数组

多维数组只是一种以编译器/机器可以理解和操作的方式“分区”内存的方法。

例如, int a[4][3][5] = 一个包含 4*3*5 (60) 个“块”的整数大小内存的数组。

使用int a[4][3][5]与普通int b[60]的优势在于它们现在是“分区的”(如果需要,更容易处理它们的“块”),并且程序现在可以执行边界检查。

事实上, int a[4][3][5]与内存中的int b[60]完全一样 -唯一的区别是程序现在管理它,就好像它们是特定大小的独立实体(特别是,四个每组五人一组)。

请记住: int a[4][3][5]int b[60]在内存中是相同的,唯一的区别是应用程序/编译器如何处理它们

{
  {1, 2, 3, 4, 5}
  {6, 7, 8, 9, 10}
  {11, 12, 13, 14, 15}
}
{
  {16, 17, 18, 19, 20}
  {21, 22, 23, 24, 25}
  {26, 27, 28, 29, 30}
}
{
  {31, 32, 33, 34, 35}
  {36, 37, 38, 39, 40}
  {41, 42, 43, 44, 45}
}
{
  {46, 47, 48, 49, 50}
  {51, 52, 53, 54, 55}
  {56, 57, 58, 59, 60}
}

由此可以清楚地看到,每个“分区”只是程序跟踪的一个数组。



句法

现在,数组在语法上与指针不同 具体来说,这意味着编译器/机器将区别对待它们。 这可能看起来很简单,但看看这个:

int a[3][3];

printf("%p %p", a, a[0]);

上面的例子打印了两次相同的内存地址,如下所示:

0x7eb5a3b4 0x7eb5a3b4

但是,只有一个可以直接分配给指针

int *p1 = a[0]; // RIGHT !

int *p2 = a; // WRONG !

为什么不能将a分配给指针而a[0]可以?

简单地说,这是多维数组的结果,我将解释原因:

在' a '的层面上,我们仍然看到我们还有另一个'维度'值得期待。 然而,在 ' a[0] ' 的层次上,我们已经在最高维度,所以就程序而言,我们只是在看一个普通的数组。

你可能会问:

如果数组是多维的,那么为它制作一个指针为什么很重要?

最好这样想:

来自多维数组的“衰减”不仅仅是一个地址,而是一个带有分区数据的地址(也就是它仍然理解它的底层数据是由其他数组组成的),它由数组在第一维之外设置的边界组成。

除非我们指定它,否则这个“分区”逻辑不能存在于指针中:

int a[4][5][95][8];

int (*p)[5][95][8];

p = a; // p = *a[0] // p = a+0

否则,数组的排序属性的意义就会丢失。

还要注意*p周围括号的使用: int (*p)[5][95][8] - 那是为了指定我们正在创建一个具有这些边界的指针,而不是具有这些边界的指针数组: int *p[5][95][8]



结论

让我们来复习:

  • 如果数组在使用的上下文中没有其他用途,则它们会衰减到地址
  • 多维数组只是数组的数组 - 因此,“衰减”地址将承担“我有子维度”的负担
  • 除非您将其提供给指针,否则维度数据不能存在于指针中。

简而言之:多维数组衰减到能够理解其内容的地址。

int *ptr= l_matrix[0];

你可以访问像

*p
*(p+1)
*(p+2)

毕竟二维数组也存储为 1-d。

G'day,

声明

static uint8_t l_matrix[10][20];

已为 10 行 20 个 unit8_t 位置预留了存储空间,即 200 个 uint8_t 大小的位置,通过计算 20 x 行 + 列来找到每个元素。

所以不

uint8_t (*matrix_ptr)[20] = l_matrix;

给你你需要的东西并指向数组第一行的第零列元素?

编辑:进一步考虑一下,根据定义,数组名称不是指针吗? 即数组名是第一个元素位置的同义词,即l_matrix[0][0]?

Edit2:正如其他人所说,评论空间太小,无法进一步讨论。 反正:

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

不为相关阵列提供任何存储分配。

如上所述,根据标准的定义,声明:

static uint8_t l_matrix[10][20];

已经预留了 200 个 uint8_t 类型的连续位置。

使用以下形式的语句引用 l_matrix:

(*l_matrix + (20 * rowno) + colno)

将为您提供在 rowno 中找到的第 colno'th 元素的内容。

所有指针操作都会自动考虑所指向对象的大小。 - K&R 第 5.4 节,第 103 页

如果手头对象的存储涉及任何填充或字节对齐移位,情况也是如此。 编译器会自动调整这些。 根据 C ANSI 标准的定义。

HTH

干杯,

在 C99(由 clang 和 gcc 支持)中,通过引用将多维数组传递给函数有一个晦涩的语法:

int l_matrix[10][20];

void test(int matrix_ptr[static 10][20]) {
}

int main(void) {
    test(l_matrix);
}

与普通指针不同,这暗示了数组大小,理论上允许编译器警告传递太小的数组并发现明显的越界访问。

可悲的是,它没有修复sizeof()并且编译器似乎还没有使用该信息,因此它仍然是一个好奇心。

您始终可以通过将数组声明为线性并自行执行 (row,col) 到数组索引计算来避免摆弄编译器。

static uint8_t l_matrix[200];

void test(int row, int col, uint8_t val)

{

   uint8_t* matrix_ptr = l_matrix;
   matrix_ptr [col+y*row] = val; // to assign a value

}

这是编译器无论如何都会做的。

初始化指向多维数组的指针的基本语法是

type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name

调用它的基本语法是

(*pointer_name)[1st index][2nd index][...]

下面是一个例子:

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

int main() {
   // The multidimentional array...
   char balance[5][100] = {
       "Subham",
       "Messi"
   };

   char (*p)[5][100] = &balance; // Pointer initialization...

   printf("%s\n",(*p)[0]); // Calling...
   printf("%s\n",(*p)[1]); // Calling...

  return 0;
}

输出是:

Subham
Messi

有效...

你可以这样做:

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

你想要一个指向第一个元素的指针,所以;

static uint8_t l_matrix[10][20];

void test(){
   uint8_t *matrix_ptr = l_matrix[0]; //wrong idea 
}

如果要使用负索引,还可以添加偏移量:

uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
matrix_ptr[-4][1]=7;

如果您的编译器给出错误或警告,您可以使用:

uint8_t (*matrix_ptr)[20] = (uint8_t (*)[20]) l_matrix;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM