结构数组中的指针问题

Issues with pointer within array of structs

提问人:Pyro 提问时间:10/24/2023 更新时间:10/24/2023 访问量:95

问:

这是我放在微控制器上的一个 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

为任何帮助而欢呼。

数组 C 指针 结构 嵌入式

评论

0赞 Fe2O3 10/24/2023
OT:重复索引的意义是什么?Address
1赞 John Bayko 10/24/2023
我能够用一些 typedef 编译您的代码来填补空白,并且它完全按预期工作(cc --version = “Apple clang version 11.0.0”)。所以我不认为这是 C 问题。
1赞 Chris Dodd 10/24/2023
像这样的代码需要在链接时设置指针值,因此链接器可能存在问题。这应该有效(并且适用于像 Linux 这样的“正常”设置),因此可能是您的微控制器链接器存在问题。
0赞 the busybee 10/24/2023
编辑您的问题并添加您使用的微控制器及其编译器,包括版本。将代码片段扩展到最小的可重现示例,显示要编译和链接的命令,以及最大警告级别及其任何输出。
0赞 Lundin 10/24/2023
@thebusybee 更有可能的是,这仅与项目设置和 CRT 有关,因此最相关的是了解 IDE 以及项目的创建方式。我发布了一个涵盖大多数微控制器系统的通用答案。

答:

1赞 Lundin 10/24/2023 #1

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 个浪费空间的字节。

评论

0赞 Pyro 10/25/2023
应该澄清一下,它是一个 8 位处理器。所以(如果我没猜错的话)char / uint8_t 类型占用 1 个字节,较大的值是它的倍数。
0赞 Pyro 10/25/2023
我认为,这个答案击中了我问题的根源。我推测这将是类似的东西。我不喜欢添加 init 函数。但是,如果必须有需求,那就这样吧。干杯。
0赞 Lundin 10/25/2023
@Pyro“char / uint8_t 类型占用 1 个字节,更大的值是其倍数”,这适用于任何类型的每台主流计算机。但是 8-bitters 通常没有对齐方式,因此您可以按照任何您想要的方式排列结构,因为不要求必须在偶数地址读取 16 位指针。
0赞 Lundin 10/25/2023
@Pyro 关于实际问题,只需在IDE中创建一个新项目(是哪一个?),然后在创建项目时注意IDE在某个“向导”中提供的任何选项。然后你就会知道这是否是问题的原因。