【问题标题】:C struct compilation failsC 结构编译失败
【发布时间】:2018-09-10 20:45:02
【问题描述】:

我正在为 Microchip Harmony Framework 开发驱动程序。它看起来像一个 Linux 驱动程序。我有一个struct (NRF24L01_MainStateInfo) 来存储驱动程序所需的所有状态,它只是一个由enums 组成的“集合”。我已经为此苦苦挣扎了 2 天:

../../../../framework/driver/nrf24l01/src/states/initialization_state/../../../drv_nrf24l01.h:51:2:错误:未知类型名称' NRF24L01_MainStateInfo''

struct 具有该类型的成员(以及错误指向的位置)如下:

#ifndef __DRV_NRF24L01_H__
#define __DRV_NRF24L01_H__

// Framework include
//...
// Project specific include
#include "src/memory_map.h"
#include "src/nrf_definitions.h"
#include "src/states/drv_nrf24l01_main_state.h" // NRF24L01_MainStateInfo defined here
#include "src/bus/drv_nrf24l01_bus.h"
//...
typedef struct _nrf24l01_driver_info {
    // Driver in use? (already configured)
    bool inUse;
    // Driver's mutex
    OSAL_MUTEX_HANDLE_TYPE drvMutex;
    // Driver configuration
    NRF24L01_ConfigData config;
    // Client count. Useless since there is a mapping 1:1 between driver and client
    uint8_t clientCnt;
    // Driver system status (returned by status)
    SYS_STATUS status;
    // FSM state
    NRF24L01_MainStateInfo state; // <-- This member generate the error

    // Bus information
    NRF24L01_BusVTable vTable;
    void *busInfo;

} NRF24L01_DriverInfo;

//...

#endif

NRF24L01_MainStateInfo 结构体在src/states/drv_nrf24l01_main_state.h 中声明如下:

#ifndef __DRV__NRF24L01_MAIN_STATE_H__
#define __DRV__NRF24L01_MAIN_STATE_H__

//#include "../../drv_nrf24l01.h"
#include "initialization_state/drv_nrf24l01_init_state.h"

struct _nrf24l01_driver_info;

/*
  Main driver state. These are the state that the developer will see.
*/
typedef enum {
  NRF24L01_MAIN_STATE_UNINITIALIZED = 0,
  NRF24L01_MAIN_STATE_INITIALIZATION,
  NRF24L01_MAIN_STATE_RUNNING,
  NRF24L01_MAIN_STATE_CLOSING,
  NRF24L01_MAIN_STATE_CLOSED
} NRF24L01_MAIN_STATE;

typedef struct _nrf24l01_mainstate_info {
  NRF24L01_MAIN_STATE mainState;
  NRF24L01_INIT_STATE initState;
} NRF24L01_MainStateInfo;

int32_t DRV_nRF24L01_MainStateTask(struct _nrf24l01_driver_info *pDrv);

#endif /* end of include guard: __DRV__NRF24L01_MAIN_STATE_H__ */

现在我想不通为什么会出现这个错误。

目录树如下:

nrf24l01 .
│   drv_nrf24l01.h
│   LICENSE
│   README.md
│
├───config
│       .gitignore
│       drv_nrf.hconfig
│       drv_nrf24l01.hconfig
│       drv_nrf24l01_idx.ftl
│
├───src
│   │   drv_nrf24l01.c
│   │   memory_map.h
│   │   nrf_definitions.h
│   │
│   ├───bus
│   │   │   drv_nrf24l01_bus.h
│   │   │
│   │   └───spi
│   │           drv_nrf24l01_spi.c
│   │           drv_nrf24l01_spi.h
│   │
│   ├───internal
│   │       drv_nrf_internal.c
│   │       drv_nrf_internal.h
│   │
│   └───states
│       │   drv_nrf24l01_main_state.c
│       │   drv_nrf24l01_main_state.h
│       │
│       ├───closing_state
│       ├───initialization_state
│       │       drv_nrf24l01_init_state.c
│       │       drv_nrf24l01_init_state.h
│       │
│       └───running_state
└───templates
        system_config.h.ftl
        system_definitions.h.INC.ftl
        system_definitions.h.OBJ.ftl
        system_init.c.DRV.ftl
        system_init.c.INIT.ftl
        system_interrupt.c.ftl
        system_tasks.c.ftl

也许我错过了什么?

编译器是xc32-gcc,uC是PIC32MX110F016B

【问题讨论】:

  • 可能不是你的问题的原因,但值得注意的是,以两个下划线或下划线后跟一个大写字母开头的符号在 C 中是保留的,自己定义它们会导致未定义的行为.
  • 您是否在包含文件中查找未定义的枚举?
  • 您能否确保在另一个文件中没有错误的标头预处理器保护 __DRV__NRF24L01_MAIN_STATE_H__ 阻止 src/states/drv_nrf24l01_main_state.h 被完全包含?
  • @Christian Gibbons 我所有的后卫现在都是DRV_NRF24L01_xxxx_H,但问题仍然存在
  • NRF24L01_INIT_STATE 在哪里以及如何定义?

标签: c struct enums microchip


【解决方案1】:

你有一个循环头依赖,这是一个糟糕的设计,几乎总是会导致失败。这个问题因为过于复杂的命名策略而变得复杂,这使得代码非常难以阅读。

这是一个基本问题,使用显着简化的名称:

文件驱动程序.h

#ifndef DRIVER_H
#define DRIVER_H

#include "state.h"

/* See answer text for an explanation of this declaration style. */
typedef struct Driver Driver;
struct Driver {
  // ...
  State state;
  // ...
};

#endif

文件状态.h

#ifndef STATE_H
#define STATE_H

// Needed because the Driver type is used in a prototype
#include "driver.h"

typedef struct State State;
struct State {
  // ...
};

// Attempt to fix circular dependency with a redundant declaration.
typedef struct Driver Driver;
int32_t stateTask(Driver* driver);
#endif

所以这两个标题相互包含。标头保护将防止它们被包含两次,但它们不能保证以正确的顺序读取声明。会发生什么取决于你#include这两个标题的顺序:

  • 如果你先#include "driver.h",它会设置它的标题保护,然后立即#include "state.h"。之前没有包含它,因此没有设置标头保护并且编译器开始处理它。它立即命中#include driver.h",但是现在设置了标头保护,即使标头还没有真正被处理,所以避免了循环包含。最终到达引用类型Driver的原型,该类型尚未定义。

    我假设您已经遇到了这个问题,因为 state.h 中有多余的 struct 声明。插入这些声明后,您可以删除 #include "driver.h",但也许您还有其他需要。

  • 另一方面,如果您首先#include "state.h",编译器会发现它的标头保护尚未设置,设置标头保护,然后继续处理标头。它立即看到#include "driver.h";该标头保护尚未设置,因此它设置该标头保护并继续该标头。现在,当它碰到driver.h 标头中的#include "state.h" 时,它什么也不做,因为现在已经设置了标头保护。

    不幸的是,#include 真的很有必要。尝试定义类型为 State 的成员时编译失败,该成员尚未定义。在这种情况下,您实际上包含了整个 State 对象,而不仅仅是使用指针,因此您不能仅仅向前声明 struct 选项卡。

简而言之,使用一个包含顺序的标题可以正常工作,但使用不同的标题会失败。不幸的是,很难预测在复杂项目中包含标头的顺序,因为标头包含并不总是可见的。它们可能出现在包含的其他标题中。这可能会导致标题包含在“错误”的顺序中。

一般来说,以特定顺序编写必须为#included 的标头不是一个好主意。它几乎总是以这种问题告终。

当您有多个相互关联的类型都被同一个小组件使用时,您最好将它们全部放在一个标题中。您仍然需要正确排序订单,但至少它们都在一个地方。在该单个头文件中,可能需要前向声明结构以允许从其他结构指向它们的指针。将类型定义放在原型之前减少了对原型引用进行前向声明的需要。

如果您有很多类型,您可以将它们的所有声明放在一个内部 project/types.h 标头中。然后,您可以将原型安排在您喜欢的任何复杂文件组织中。这是一个很常见的范式。对于外部原型标头(即声明旨在全局可见的函数的标头),您可以通过前向声明原型使用的结构来减少混乱。假设原型只使用了一个指向结构体的指针,这当然是最常见的,那么就不需要让结构体的定义可见了。

警告: 意见风格建议如下。如果你不喜欢那种东西,你可以在这里停止阅读。

一旦你整理好你的标题,假设它们只是在内部使用,你可以通过从内部结构和枚举名称中删除不必要的前缀来为你自己和你的读者简化事情。 typedef、结构和联合标签以及枚举没有链接;它们不能泄漏到另一个单独编译的翻译单元中。因此,如果它们不是供全球使用的,则无需使它们全球唯一。

无论您在其他人的代码中看到什么,绝对没有必要使 typedef 名称与结构标记不同,即使您有一天打算使用 C++ 进行编译。在 C 中,名称位于两个完全独立的名称空间中;结构标记仅在以标记 struct 开头时才会被识别。所以

typedef struct MyStructure MyStructure;

绝对有效。事实上,即使 struct MyStructure 还没有被充实,它也是有效的,这使得包含指向相同类型的指针的结构类型很方便。

我倾向于使用上面代码 sn-ps 中显示的样式,总是将typedef 放在结构定义之前。我发现这比在结构定义的末尾使用 typedef 名称更具可读性,即使在我的风格中名称总是相同的。此外,转发typedefs 可以简单地复制到需要它们的标题中。如果你多次typedef 同一个名字到同一个类型,C 不会抱怨,所以这是完全安全的。

【讨论】:

  • 非常感谢您的回答,今晚我将仔细研究并按照您的建议进行一些重构!我会告诉你的;)
  • 好吧,花了我一段时间,但我做了一些测试,是的,就是这样@rici!无论如何,我决定重新编写驱动程序的某些部分并尽可能简化它。再次感谢您的宝贵建议!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-21
  • 2011-01-22
  • 1970-01-01
  • 2021-10-07
  • 2013-07-18
  • 2019-04-19
  • 1970-01-01
相关资源
最近更新 更多