我需要一个指向静态二维数组的指针。如何实现这一点?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
我遇到各种错误,例如:
- 警告:指针类型不兼容的赋值
- 下标值既不是数组也不是指针
- 错误:无效使用可变大小数组成员
我需要一个指向静态二维数组的指针。如何实现这一点?
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] = ...;
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性质),如果你无法更改数组类型,则这是拯救你的最后一种选择 :)uint8_t *d[20]
混淆了,后者创建了一个指向uint8_t的3个指针的数组,但在这种情况下不起作用。 - Palo为了充分理解这个问题,你必须掌握以下概念:
首先(已经有足够的人宣扬过),数组不是指针。相反,在大多数用法中,它们会“退化”为其第一个元素的地址,可以赋给指针:
int a[] = {1, 2, 3};
int *p = a; // p now points to a[0]
我认为它的工作方式是这样的,以便可以访问数组的内容而不必复制所有内容。这只是数组类型的一种行为,不意味着它们是同一件事。
多维数组只是一种以编译器/计算机可以理解和操作的方式“分割”内存的方法。
例如,int a [4] [3] [5]
= 一个包含4 * 3 * 5(60)个整数大小内存块的数组。
与使用普通的int b [60]
相比,int a [4] [3] [5]
的优点在于它们现在被“分割”了(如果需要,更容易处理它们的“块”),并且程序现在可以执行边界检查。
实际上,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 *p1 = &(a[0]); // RIGHT !
,实际上它与 int *p1 = a;
是相同的。 - 2501In
int *ptr= l_matrix[0];
*p
*(p+1)
*(p+2)
毕竟,2维数组也是存储为1维的。
*(p+k)
就是 p[k]
的简写! - John Stroodstatic uint8_t l_matrix[10][20];
已为10行20个uint8_t位置设置了存储空间,即200个uint8_t大小的位置,每个元素可通过计算20 x 行 + 列来找到。
所以不会
uint8_t (*matrix_ptr)[20] = l_matrix;
能否给你所需的东西并指向数组第一行的零列元素?
编辑:进一步思考,一个数组名字不是定义为指针吗?也就是说,数组的名称是第一个元素的位置的同义词,即l_matrix[0][0]?
编辑2:正如其他人所提到的,评论空间对于进一步讨论来说有点太小了。无论如何:
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个元素的内容。
所有指针操作都会自动考虑所指对象的大小。- K&R第5.4节,第103页
如果在存储手头对象时涉及填充或字节对齐偏移,则情况也是如此。编译器将自动调整这些内容。根据C ANSI标准的定义。
希望能帮到您。
祝好,
在C99中(由clang和gcc支持),有一种通过引用传递多维数组给函数的模糊语法:
int l_matrix[10][20];
void test(int matrix_ptr[static 10][20]) {
}
int main(void) {
test(l_matrix);
}
sizeof()
的问题,而且编译器似乎还没有使用这些信息,所以它仍然只是一个好奇心。static 10
是某种保证,至少有10个元素存在,这意味着大小并不是固定的。 - blussstatic
,数组就不会被按引用传递,但这是不正确的。数组总是被按引用传递的。原始问题询问了一个不同的用例 - 在同一函数/命名空间中使用补充指针访问2D数组的元素。 - Palostatic 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)[第一维大小][第二维大小][..] = &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
它起作用了...
static uint8_t l_matrix[10][20];
void test(){
uint8_t *matrix_ptr = l_matrix[0]; //wrong idea
}
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
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;
c
,因此答案应该使用相同的语言。请注意标签。 - 2501