Dil*_*ill 110 c arrays pointers
我需要一个指向静态二维数组的指针.这是怎么做到的?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
Run Code Online (Sandbox Code Playgroud)
我得到各种错误,如:
Joh*_*itb 129
在这里,您想要指向数组的第一个元素
uint8_t (*matrix_ptr)[20] = l_matrix;
Run Code Online (Sandbox Code Playgroud)
使用typedef,这看起来更干净
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
Run Code Online (Sandbox Code Playgroud)
然后你可以再次享受生活:)
matrix_ptr[0][1] = ...;
Run Code Online (Sandbox Code Playgroud)
要注意C 中的指针/数组世界,这个问题很多.
在这里回顾一些其他答案,因为评论字段太短,无法在那里做.提出了多种替代方案,但没有显示它们的行为方式.他们是这样做的
uint8_t (*matrix_ptr)[][20] = l_matrix;
Run Code Online (Sandbox Code Playgroud)
如果您修复了错误并添加了地址操作符,&就像在下面的代码段中一样
uint8_t (*matrix_ptr)[][20] = &l_matrix;
Run Code Online (Sandbox Code Playgroud)
然后,那个创建一个指向20 uint8_t数组类型的元素的不完整数组类型的指针.因为指针是一个数组数组,所以你必须使用它来访问它
(*matrix_ptr)[0][1] = ...;
Run Code Online (Sandbox Code Playgroud)
因为它是指向不完整数组的指针,所以不能作为快捷方式
matrix_ptr[0][0][1] = ...;
Run Code Online (Sandbox Code Playgroud)
因为索引要求知道元素类型的大小(索引意味着向指针添加整数,所以它不适用于不完整的类型).请注意,这仅适用于C,因为T[]并且T[N]是兼容类型.C++不具有的概念兼容的类型,因此它会拒绝代码,因为T[]和T[10]有不同的类型.
以下替代方法根本不起作用,因为当您将其视为一维数组时,数组的元素类型不是 uint8_t,但是uint8_t[20]
uint8_t *matrix_ptr = l_matrix; // fail
Run Code Online (Sandbox Code Playgroud)
以下是一个很好的选择
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
Run Code Online (Sandbox Code Playgroud)
你可以访问它
(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now
Run Code Online (Sandbox Code Playgroud)
它的好处是可以保留外部尺寸的大小.所以你可以在上面应用sizeof
sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20
Run Code Online (Sandbox Code Playgroud)
还有一个答案是利用数组中的项目是连续存储的这一事实
uint8_t *matrix_ptr = l_matrix[0];
Run Code Online (Sandbox Code Playgroud)
现在,这正式只允许您访问二维数组的第一个元素的元素.也就是说,以下条件成立
matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid
matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior
Run Code Online (Sandbox Code Playgroud)
您会注意到它可能会起作用10*20-1,但如果您抛出别名分析和其他积极的优化,某些编译器可能会做出可能会破坏该代码的假设.话虽如此,我从来没有遇到过失败的编译器(但是我再也没有在实际代码中使用过这种技术),甚至C FAQ都包含了这种技术(警告其UB'ness) ),如果你不能改变数组类型,这是保存你的最后一个选项:)
Sup*_*Cat 25
要完全理解这一点,您必须掌握以下概念:
首先(并且它已被充分宣传),数组不是指针.相反,在大多数用途中,它们"衰减"到第一个元素的地址,可以将其分配给指针:
int a[] = {1, 2, 3};
int *p = a; // p now points to a[0]
Run Code Online (Sandbox Code Playgroud)
我假设它以这种方式工作,以便可以访问数组的内容而无需复制所有内容.这只是数组类型的行为,并不意味着它们是相同的东西.
多维数组只是一种以编译器/机器可以理解和操作的方式"分区"内存的方法.
例如,int a[4][3][5]=一个包含整数大小内存的4*3*5(60)'块'的数组.
使用int a[4][3][5]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}
}
Run Code Online (Sandbox Code Playgroud)
由此,您可以清楚地看到每个"分区"只是程序跟踪的数组.
现在,数组在语法上与指针不同.具体来说,这意味着编译器/机器将以不同方式对待它们.这看起来似乎没什么问题,但看看这个:
int a[3][3];
printf("%p %p", a, a[0]);
Run Code Online (Sandbox Code Playgroud)
上面的例子打印了两次相同的内存地址,如下所示:
0x7eb5a3b4 0x7eb5a3b4
Run Code Online (Sandbox Code Playgroud)
但是,只有一个可以直接分配给指针:
int *p1 = a[0]; // RIGHT !
int *p2 = a; // WRONG !
Run Code Online (Sandbox Code Playgroud)
为什么不能 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
Run Code Online (Sandbox Code Playgroud)
否则,数组的排序属性的含义将丢失.
还要注意括号的使用*p:int (*p)[5][95][8]- 这是指定我们用这些边界制作一个指针,而不是带有这些边界的指针数组:int *p[5][95][8]
让我们来复习:
简而言之:多维数组衰减到能够理解其内容的地址.
小智 7
在
int *ptr= l_matrix[0];
Run Code Online (Sandbox Code Playgroud)
你可以访问
*p
*(p+1)
*(p+2)
Run Code Online (Sandbox Code Playgroud)
在所有二维数组也存储为1-d之后.
天儿真好,
声明
static uint8_t l_matrix[10][20];
Run Code Online (Sandbox Code Playgroud)
为10行20个unit8_t位置预留了存储空间,即200个uint8_t大小的位置,每个元素通过计算20 x row + column找到.
所以没有
uint8_t (*matrix_ptr)[20] = l_matrix;
Run Code Online (Sandbox Code Playgroud)
给你你需要的东西,并指向数组第一行的列零元素?
编辑:进一步考虑这个,根据定义,不是数组名称,指针?也就是说,数组的名称是第一个元素位置的同义词,即l_matrix [0] [0]?
编辑2:正如其他人所说,评论空间有点太小,无法进一步讨论.无论如何:
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
Run Code Online (Sandbox Code Playgroud)
没有为相关阵列提供任何存储分配.
如上所述,并按照标准的定义,声明:
static uint8_t l_matrix[10][20];
Run Code Online (Sandbox Code Playgroud)
已经预留了200个uint8_t类型的连续位置.
使用以下形式的语句引用l_matrix:
(*l_matrix + (20 * rowno) + colno)
Run Code Online (Sandbox Code Playgroud)
将为您提供行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);
}
Run Code Online (Sandbox Code Playgroud)
与普通指针不同,这暗示了数组大小,理论上允许编译器警告传递太小的数组并发现明显的超出边界访问.
遗憾的是,它没有修复sizeof(),编译器似乎还没有使用这些信息,所以它仍然是一种好奇心.