我需要一个指向静态二维数组的指针。怎么做?
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
}
我收到各种错误,例如:
Answers:
在这里,您想指向数组的第一个元素
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的警告) ),如果您不能更改数组类型,这是保存您的最后一个选择:)
uint8_t *d[20]
,后者创建了一个由3个指向uint8_t的指针组成的数组,但是在这种情况下不起作用。
要完全理解这一点,您必须掌握以下概念:
首先(已经讲到足够了),数组不是指针。相反,在大多数使用中,它们“衰减”到其第一个元素的地址,该地址可以分配给指针:
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]
vs vs plain 的优点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]
但是,在' ' 级别,我们已经处于顶层,因此就程序而言,我们只是在看普通数组。
您可能会问:
如果数组是多维的,那么为什么要为其创建指针呢?
最好这样考虑:
来自多维数组的“衰变”不仅是地址,而且是具有分区数据的地址(AKA仍然可以理解其基础数据是由其他数组构成的),它由数组在第一维之外设置的边界组成。
除非我们指定它,否则该“分区”逻辑不能存在于指针中:
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;
G'day,
报关单
static uint8_t l_matrix[10][20];
已为20个unit8_t位置的10行预留了存储空间,即200 uint8_t大小的位置,每个元素都是通过计算20 x row + column来找到的。
所以不
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标准的定义。
高温超导
干杯,
在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个元素,这再次意味着大小是不确定的。
static
,数组将不会通过引用传递,这是不正确的。无论如何,数组都是通过引用传递的。最初的问题询问了一个不同的用例-在同一函数/名称空间中使用辅助指针访问2D数组的元素。
您始终可以通过将数组声明为线性数组并自己执行(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;