C结构编译失败

时间:2018-09-10 20:45:02

标签: c struct enums microchip

我正在为 Microchip Harmony Framework 开发驱动程序。它看起来像Linux驱动程序。我有一个structNRF24L01_MainStateInfo)存储驱动程序所需的所有状态,它只是由enum组成的“集合”。我在 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

1 个答案:

答案 0 :(得分:3)

您有一个循环的标头依赖项,这是错误的设计,几乎总是导致失败。过于复杂的命名策略使问题变得复杂,这使得代码确实很难阅读。

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

文件驱动程序。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类型的原型,该类型尚未定义。

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

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

    不幸的是,#include确实是必要的。尝试定义类型State的成员(尚未定义)时,编译将失败。在这种情况下,您实际上是在包含整个State对象,而不仅仅是使用指针,因此仅向前声明struct选项卡就无法摆脱困境。

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

通常,编写必须以特定顺序#include d的标头不是一个好主意。几乎总是以这种问题结束。

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

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

警告: 以下是有针对性的样式建议。如果您不喜欢这种东西,可以在这里停止阅读。

一旦您整理了标头(假设它们仅在内部使用),则可以通过从内部结构和枚举名称中删除不必要的前缀来简化您自己和您的读者的工作。 Typedef,结构和联合标签以及枚举没有链接;它们不能泄漏到另一个单独编译的翻译单元中。因此,如果它们不打算在全球范围内使用,则无需使它们在全球范围内都是唯一的。

无论您在别人的代码中看到什么,即使您有朝一日打算使用C ++进行编译,也绝对不需要使typedef名称与struct标记不同。在C语言中,名称位于两个完全独立的命名空间中。只有在标记struct之前,才可以识别struct标记。所以

typedef struct MyStructure MyStructure;

是绝对有效的。实际上,即使struct MyStructure尚未充实,它也是有效的,这使其对于包含指向同一类型的指针的结构类型很方便。

我倾向于使用上面代码片段中显示的样式,总是将typedef放在结构定义之前。我发现比在结构定义的末尾放下typedef名称更具可读性,即使在我的样式中,名称始终相同。此外,前typedef可以简单地复制到需要它们的标头中。如果您多次typedef为同一类型使用相同的名称,C不会抱怨,因此这是完全安全的。