我有一个指向数组的指针。
uint8_t *source[5];
抱歉,但这不是指向数组的指针。它是一个包含5个未初始化的指向uint8_t
的指针的数组,因此您应该对其进行初始化。
这是一个指向数组的指针:
uint8_t (*source)[5];
(在类型表达式中,[]
运算符的优先级高于 *
运算符)
...这里到底发生了什么?
嗯,基于你错误的假设,所有的东西都是不正确的。
// Initial
printf("%p\n", (void *)source); // 786796896
// Offset by 2
printf("%p\n", (void *)(source + 2)); // 786796912 (unexpected, more than 2)
source + 2
是数组 source
的第三个元素的地址,它是一个指针数组,因此它比 source
本身偏移了两个指针位置(在64位架构中为16字节--您得到的值--,在32位架构中为8字节)
只需尝试使用所提议的声明,您将获得预期结果。
$ a.out
0x560bee9df060
0x560bee9df06a
$ _
(10个位置,正如预期的两个5 uint8_t
元素数组)
但您可以自行检查。
如果您在source
本身上使用sizeof
运算符,则会得到一个数组的大小(在64位架构上为5个指针或40个字节),而正如您在问题中所述,它应该是单个指针的大小(在64位架构中为8个字节)。如果您使用建议的声明进行声明,则将获得8,因为这是预期的,而如果您执行sizeof *source
(指向source
的对象的大小),在这种情况下,您将获得5(五个uint8_t
值的数组的大小)。试一试,看看吧。
关于在问题评论中讨论的对齐问题,这是最后的说明。
声明的对象是指针数组,而不是指向包含五个
uint8_t
的数组的指针,因此其地址(传递给
printf
的值确实对齐且有效(它确实是在堆栈中创建变量
source
的地址),并且在使用该地址时不会产生U.B.,因为数组内容本身未被使用,因此保持未初始化状态并不会引起U.B.)
如果正确声明为指向数组的指针,则打印其未初始化的值---唯一不可预测的是该值本身---但这不需要解引用指针或对齐(这不是问题,请参见下一个)。在这种情况下,应避免对未初始化指针变量进行指针算术运算,因为不能保证指针的目标值是有效地址,因此存在一些(通常不会有害的)U.B.(这是因为原始未初始化值本身也可能是无效地址,并且在仅处于未初始化状态时不应引起U.B.。
(void *)
转换本身通常不会引起任何U.B.)
如果声明为指向数组的指针,则再次没有对齐问题,因为指针的基本类型是一个具有1(一个)对齐的
uint8_t
数组,因此任何地址都应该是可接受的。由于未解引用指针,因此它的行为类似于任何未初始化变量,因此不会引起U.B.
uint8_t *ptr = source;
这是一个错误,你的编译器会告诉你。@user16217248 解释了你看到的值,但这可能是未定义的行为,只是碰巧做了你想要的事情。 - pmacfarlane