我发现了一些让我有点好奇的代码:有一个头文件,它只声明了用C编写的嵌入式系统的代码中使用的 struct .

以下是标题中的代码示例:

#ifdef _structures_h_
 
extern struct{
    uint16_t  State_ON;
    uint16_t  State_OFF;
    uint16_t  State_HI;    
    uint16_t  Counter;
}Switch;

#endif


#ifndef _structures_h_
#define _structures_h_

struct{
    uint16_t  State_ON;
    uint16_t  State_OFF;
    uint16_t  State_HI;    
    uint16_t  Counter;
}Switch;

#endif

在使用这些 struct 的源文件中,其中一些文件的包含方式如下所示:

#define _structures_h_
#include "structures.h"

在其他情况下,只包含头文件,如下所示:

#include "structures.h"
  • 我的疑问是,是否有某种理由在同一个头文件中以两种方式声明 struct ?

  • 在代码中声明与外部相同的 struct 与在头文件管理器中声明相同的 struct 有什么不同或相关影响吗?

这是我第一次发现这样的情况,我无法找到合理的解释来使用这一点.

推荐答案

这两个代码段之间的区别在于,在第一个代码段(关键字为extern的代码段)中, struct 变量是declared,而不是defined.也就是说,代码指定Switch变量为defined elsewhere(在另一个翻译单元中).然而,在第二节中,Switch变量被正式声明为and defined--或者至少定义了tentatively个;如果翻译单元的编译完成而没有发生其他full个定义(即具有初始化器的定义),则该暂定定义将成为actual个定义.

在第二个条件块中,_structures_h_令牌定义很奇怪;而且,除非标头有比您所显示的更多的内容,否则我真的看不出它有什么意义:

  1. 如果single TU多次包含标头,则无论有没有该定义,都会使用不同的类型重新定义Switch变量(即使这些类型正好有equivalent个定义,但它们仍然是单独的类型);这种重新定义是错误的.
  2. 如果头包含在multiple TUs中,则一个中宏的定义将不会"传递"到任何其他变量,因此您仍然(可能)拥有每个TU中包含头的Switch变量的多个实例,而不是第一个themselves定义_structures_h_标记.

但我同意您和其他回答者的看法,这样的代码既令人困惑又容易出错:例如,在多个转换单元without a preceding 100 definition中使用该标头将导致Switch变量的多个定义.

此外,与其使用#ifndef ...块,简单的#else就足够了,让事情更清楚(IMHO).

以下是标题的简短、带注释版本:

#ifdef _structures_h_
extern struct {
    uint16_t  State_ON;
    uint16_t  State_OFF;
    uint16_t  State_HI;
    uint16_t  Counter;
} Switch; // Already defined elsewhere - just need the declaration
#else
struct {
    uint16_t  State_ON;
    uint16_t  State_OFF;
    uint16_t  State_HI;
    uint16_t  Counter;
} Switch; // Not yet defined, so we provide a "tentative" definition
#endif

或者,为了避免 struct 的多个规范(以及可能导致的错误),一种更简单的方法是定义一个单独的"链接"宏,该宏要么为extern,要么为零:

#ifdef _structures_h_
#define SW_LINKAGE extern
#else
#define SW_LINKAGE /*  */
#endif
SW_LINKAGE struct {
    uint16_t  State_ON;
    uint16_t  State_OFF;
    uint16_t  State_HI;
    uint16_t  Counter;
} Switch;

C++相关问答推荐

C中空终止符后面的数字?

为什么信号量为空= 0,而不是阻塞?

如果实际的syscall是CLONE(),那么为什么strace接受fork()呢?

在C中使用动态内存分配找到最小的负数

N的值设置为0或1(未定义的行为),而我正在try 学习realloc和Malloc的用法

预先分配虚拟地址空间的区域

GTK函数调用将完全不相关的char* 值搞乱

为什么数组的最后一个元素丢失了?

如何只获取字符串的第一个单词,然后将其与c中的另一个单词进行比较?

变量的作用域是否在C中的循环未定义行为或实现定义行为的参数中初始化?

For循环不会迭代所有字符串字符吗?(初学者问题)

为什么编译器不能简单地将数据从EDI转移到EAX?

表达式x&;&;(~x)应该返回1还是0?它依赖于编译器吗?

变量值不正确的问题

STM32:代码的执行似乎取决于它在闪存中的位置

struct 中的qsort,但排序后的 struct 很乱

Ubuntu编译:C中的文件格式无法识别错误

使用 c 中的 write() 函数将非 ASCII 字符写入标准输出

使用 _Atomic float 时,MSVC 编译的代码会命中调试断言

比 * 更快的乘法