提问人:Pyro 提问时间:10/24/2023 更新时间:10/24/2023 访问量:95
结构数组中的指针问题
Issues with pointer within array of structs
问:
这是我放在微控制器上的一个 C 问题。
我有一个结构,其中包含我的硬件实例“Node”的值,每个值都与一个“寄存器”相关。
“register”是一个结构,其中包含有关读/写权限的详细信息。状态标志...etc 和指向 Node 结构中相关值的指针。
我有一个这些寄存器的数组。请参阅下面的代码:
typedef struct
{
uint8_t Address;
uint32_t *Value;
typebool ReadOnly;
uint8_t Type;
typebool QueryRequest; //Has host queried this register?
typebool UpdateOccured; //has a register been updated?
} typeRegister;
struct typeNode
{
uint32_t FlagSetter;
uint32_t FlagClearer;
uint32_t Flags;
uint32_t Colour;
uint32_t HeldFor;
uint32_t Team;
uint32_t UpdateOccured; //has a register been updated?
} Node;
typeRegister Registers[6] =
{
{0,&Node.FlagSetter,False,type_int32,False,False},
{1,&Node.FlagClearer,False,type_int32,False,False},
{2,&Node.Flags,False,type_int32,False,False},
{3,&Node.Colour,False,type_int32,False,False},
{4,&Node.HeldFor,False,type_int32,False,False},
{5,&Node.Team,False,type_int32,False,False}
};
根据我对指针的理解,这应该允许使用语法或 .uint32_t Value3 = *Registers[3].Value
*Registers[3].Value = 100
但是,当我尝试执行此操作时,从未正确分配或读取 Value。
但是,如果我在运行时分配指针(即 ),它确实有效。Registers[3].Value = &Node.Colour;
在运行时之外的结构中声明指针是否有限制?
我已经做了一堆值和指针设置,获取和分配,似乎如果我分配寄存器[n]。Value = &Node.Colour,它可以工作,但如果在编译时分配,则不行:
uint32_t Value = 0;
Value = Node.Colour; // Value == 0xAB || &Node.Colour == 0x0047
uint32_t *ValuePtr;
ValuePtr = &Node.Colour; // ValuePtr == 0x0047 | *ValuePtr == 0xAB
uint32_t Value2 = *ValuePtr; // Value2 == 0xAB
uint32_t Value3 = *Registers[3].Value; // Value3 == 0xFE04FE00
uint32_t *ValuePtr2;
ValuePtr2 = Registers[3].Value; // ValuePtr2 == 0x0000 | *ValuePtr2 = 0x00470000 <-- WRONG
Registers[3].Value = &Node.Colour; // Assign pointer at run time
uint32_t *ValuePtr3;
ValuePtr3 = Registers[3].Value; // ValuePtr3 == 0x0047 | *ValuePtr3 == 0xAB <-- RIGHT
为任何帮助而欢呼。
答:
MCU项目,无论工具链如何,在创建时都有两个相互排斥的替代方案,这是很常见的:
- 一个名为“快速/最小启动”的选项。这样可以更快地从上电开始启动,但偏离标准 C。
- 一个名为“标准 C/ANSI C/strict”等的选项。这将创建符合要求的 C 语言行为。
这意味着 C 标准要求所有具有静态存储持续时间的变量 - 那些在文件范围或 as 函数之外声明的变量 - 应在调用 main() 之前的某个点初始化。static
对于微控制器来说,这意味着这些变量需要通过在 main() 启动之前运行的 CRT 启动代码将其值从闪存复制到 RAM。这需要一些时间才能完成,具体取决于有多少变量。(在 C++ 的情况下,默认构造函数也会被执行,从而增加更多的开销。一些特别糟糕的 CRT 首先执行此初始化,同时在一些缓慢的内部 RC 系统时钟上运行,然后只有在您在 main() 中明确告诉它这样做时才会切换到快速振荡器设置。
为了避免启动缓慢引起的问题,存在“快速/最小”选项。如果在您的情况下启用,则意味着将忽略初始化静态存储持续时间变量的代码。比如这样:
typeRegister Registers[6] =
{
{0,&Node.FlagSetter,False,type_int32,False,False},
...
你不会收到任何关于这一点的编译器警告,编译器会静默地忽略初始值设定项列表。相反,您必须在运行时分配它:
Registers[0] = (Registers){0,&Node.FlagSetter,False,type_int32,False,False};
...
因此,如果使用最小的非标准设置,则必须相应地调整编写代码的方式。例如,通过将上述运行时初始化放在适用驱动程序的某个 init() 函数中。
或者,如果您不希望出现此行为,请在创建项目时选择标准 C 模式。
与您的问题无关,结构成员在对齐方面组织得很差。如果你使用没有对齐的 8/16 苦涩,这并不重要,但如果你使用有对齐的 MCU,比如 Cortex M,这确实很重要。在这种情况下,结构成员应该得到适当的组织,以免不必要地导致膨胀填充:
typedef struct
{
uint32_t *Value;
uint8_t Address;
typebool ReadOnly;
uint8_t Type;
typebool QueryRequest; //Has host queried this register?
typebool UpdateOccured; //has a register been updated?
} typeRegister;
这在功能方面是 100% 等效的,但不会在 32 bitter/32 位地址总线上为每个结构对象分配 3 个浪费空间的字节。
评论
Address