[英]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.