我需要一个指向静态二维数组的指针。这是怎么做的?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
我得到了各种各样的错误,比如:
灵活数组警告:来自不兼容指针type
的使用无效
发布于 2009-06-27 13:37:28
在这里,您希望创建一个指向数组第一个元素的指针
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中的pointer/array world,这里面有很多混淆。
编辑
在这里查看一些其他答案,因为注释字段太短,无法在那里完成。提出了多种替代方案,但没有展示它们的行为方式。他们是这样做的:
uint8_t (*matrix_ptr)[][20] = l_matrix;
如果修复了错误并添加了操作符&
的address-of,如以下代码片段所示
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‘性的警告),如果您不能更改数组类型,这是拯救您的最后一个选择:)
发布于 2016-01-10 14:21:08
要完全理解这一点,您必须掌握以下概念:
数组不是指针!
首先,数组不是指针,这一点已经宣传得够多了。相反,在大多数情况下,它们“衰减”到它们的第一个元素的地址,该地址可以分配给一个指针:
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]
结论
让我们回顾一下:
如果没有其他用途,子数据数组就会衰减为地址,因为使用的context
简而言之:多维数组衰减为能够理解其内容的地址。
发布于 2012-02-18 08:13:07
在……里面
int *ptr= l_matrix[0];
你可以像这样访问
*p
*(p+1)
*(p+2)
毕竟,2维数组也存储为1-d。
https://stackoverflow.com/questions/1052818
复制相似问题