CS+中,全局变量的正确管理方式

你有很多 .c 和 .h 文件,全局变量需要在多个 .c 文件之间共享。使用 GLOBAL_VARS 开关的目的是:让变量只被定义一次,其他文件只做声明。

下面是完整的操作步骤。


一、总体原则

  1. 所有全局变量集中定义在一个头文件里 (例如 global_vars.h)。

  2. 只在一个 .c 文件里定义 GLOBAL_VARS,这个文件负责真正分配内存。

  3. 其他所有 .c 文件只包含这个头文件 ,自动获得 extern 声明。

  4. 不要在多个头文件里定义同一个变量,否则会重复定义。


二、具体操作步骤

第 1 步:创建一个全局变量头文件

新建 global_vars.h(文件名随意,例如 user_sysrun.h),内容如下:

复制代码
#ifndef _GLOBAL_VARS_H_
#define _GLOBAL_VARS_H_

/* ===== 类型定义需要的头文件 ===== */
#include "typedef.h"       // 或 r_cg_macrodriver.h,看你工程用哪个
#include "user_sysrun.h"   // 如果有结构体定义在这里

/* ===== GLOBAL_VARS 开关 ===== */
#ifndef GLOBAL_VARS
#define ramExtern              // 空:在定义文件里,变量会被真正定义
#else
#ifndef _User_Extern_
#define _User_Extern_
#define ramExtern extern       // extern:在其他文件里,只是声明
#endif
#endif

/* ===== 所有全局变量在这里统一声明/定义 ===== */
ramExtern stc_Sys_Uart UART3;
ramExtern stc_Sys_Uart UART2;
ramExtern stc_Sys_Uart UART0;
ramExtern TYPE_WORD    FLAG0;
ramExtern uint8_t      ModbusBuffer[512];
ramExtern uint16_t     Para_Max[Para_Number];
ramExtern uint16_t     Para_Min[Para_Number];
// ... 其他全局变量

#endif

关键点:

  • 所有全局变量的声明和定义都写在这里。

  • ramExtern 根据 GLOBAL_VARS 是否定义,展开为空或 extern。

  • 头文件保护宏 _GLOBAL_VARS_H_ 防止重复包含。


第 2 步:在一个 .c 文件里定义 GLOBAL_VARS

选一个 .c 文件(例如 global_vars.c 或 user_sysrun.c),在包含头文件之前 定义 GLOBAL_VARS:

复制代码
/* global_vars.c */
#define GLOBAL_VARS           // ← 关键:只有这一个文件定义它
#include "global_vars.h"      // ← 包含头文件,变量在这里真正定义并分配内存

/* 这个文件里不需要写其他代码,变量已经分配好了 */

效果:

  • ramExtern 展开为空。

  • stc_Sys_Uart UART3; 等语句变成真正的变量定义,分配 RAM。

  • 整个工程里,UART3、UART2、UART0 等只在这里被定义一次。


第 3 步:其他 .c 文件正常包含头文件

其他所有需要使用这些全局变量的 .c 文件,直接包含 global_vars.h 即可,不要定义 GLOBAL_VARS:

复制代码
/* uart.c */
#include "global_vars.h"      // 不定义 GLOBAL_VARS

void UartRXD(stc_Sys_Uart *UART)
{
    UART3.RxBuf[0] = 0x55;    // 直接使用,编译时是 extern 声明
    // ...
}
复制代码
/* main.c */
#include "global_vars.h"

void main(void)
{
    UART3.time_NoWork = 0;
    FLAG0.word = 0x0000;
    // ...
}

效果:

  • ramExtern 展开为 extern。

  • 这些文件里的变量只是声明,不分配内存,链接时指向 global_vars.c 里定义的那一份。


三、目录结构示意

复制代码
Project/
├── global_vars.h        ← 所有全局变量集中声明/定义
├── global_vars.c        ← 唯一定义 GLOBAL_VARS 的文件
├── typedef.h            ← 类型定义(uint8_t、结构体等)
├── uart.c               ← #include "global_vars.h"
├── main.c               ← #include "global_vars.h"
├── display.c            ← #include "global_vars.h"
└── ...

四、常见问题与注意事项

1. 为什么只能在一个文件里定义 GLOBAL_VARS?

如果两个 .c 文件都定义 GLOBAL_VARS,那么 ramExtern 都展开为空,两个文件都会定义 UART3,链接时报错:

复制代码
Error: Symbol UART3 defined more than once

所以只能有一个文件定义它。

2. GLOBAL_VARS 必须放在 #include "global_vars.h" 之前

复制代码
#define GLOBAL_VARS           // 必须在前面
#include "global_vars.h"      // 在后面

顺序反了,GLOBAL_VARS 没生效,头文件里的 ramExtern 仍然是 extern,变量不会被定义,链接时报"未定义符号"。

3. 全局变量初始化只能在定义的文件里写

复制代码
/* global_vars.c */
#define GLOBAL_VARS
#include "global_vars.h"

/* 可以在这里给变量赋初值 */
uint8_t ModbusBuffer[512] = {0};   // OK

其他文件只能声明,不能赋初值。

4. 头文件保护宏不能重复

每个头文件都要有独立的保护宏,例如:

复制代码
/* global_vars.h */
#ifndef _GLOBAL_VARS_H_
#define _GLOBAL_VARS_H_
...
#endif

不要和别的头文件用同一个保护宏名。

5. 结构体类型要先定义

stc_Sys_Uart、TYPE_WORD 这些类型必须先定义,global_vars.h 才能使用。通常:

复制代码
/* global_vars.h */
#include "typedef.h"           // 里面定义 stc_Sys_Uart、TYPE_WORD
#include "user_sysrun.h"
...

或者在 global_vars.h 里直接包含定义这些类型的头文件。

6. 用 extern 声明数组时不要写大小

复制代码
/* 头文件里 */
extern uint8_t ModbusBuffer[];   // 可以省略大小,也可以写
extern uint8_t ModbusBuffer[512]; // 也行

但在定义的文件里必须给出大小:

复制代码
uint8_t ModbusBuffer[512];       // 定义,必须给大小

五、完整示例

typedef.h(类型定义)

复制代码
#ifndef _TYPEDEF_H_
#define _TYPEDEF_H_

typedef unsigned char  uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long  uint32_t;

typedef struct _Uart
{
    uint16_t RxBufLen;
    uint16_t RxDataLen;
    uint8_t  RxBuf[64];
    uint8_t  flag_RXDEnd;
    // ...
} stc_Sys_Uart;

typedef union
{
    struct
    {
        uint16_t no0:1;
        uint16_t no1:1;
        // ...
    } Bit;
    uint16_t word;
} TYPE_WORD;

#endif

global_vars.h(全局变量集中管理)

复制代码
#ifndef _GLOBAL_VARS_H_
#define _GLOBAL_VARS_H_

#include "typedef.h"

#ifndef GLOBAL_VARS
#define ramExtern
#else
#ifndef _User_Extern_
#define _User_Extern_
#define ramExtern extern
#endif
#endif

ramExtern stc_Sys_Uart UART3;
ramExtern stc_Sys_Uart UART2;
ramExtern stc_Sys_Uart UART0;
ramExtern TYPE_WORD    FLAG0;
ramExtern uint8_t      ModbusBuffer[512];

#endif

global_vars.c(唯一定义处)

复制代码
#define GLOBAL_VARS
#include "global_vars.h"
/* 变量在这里分配内存 */

uart.c(使用)

复制代码
#include "global_vars.h"

void UartRXD(stc_Sys_Uart *UART)
{
    if (UART->flag_RXDEnd == 1)
    {
        // 直接使用 UART3、UART2、UART0
    }
}

main.c(使用)

复制代码
#include "global_vars.h"

void main(void)
{
    UART3.time_NoWork = 0;
    FLAG0.word = 0x0000;
}

六、总结

问题 答案
全局变量在哪里定义 集中写在一个头文件里,例如 global_vars.h
GLOBAL_VARS 在哪里定义 只在一个 .c 文件里,在包含头文件之前
其他文件怎么用 直接 #include "global_vars.h",不定义 GLOBAL_VARS
为什么这样做 避免重复定义,避免手写 extern,统一管理
注意事项 只能一个文件定义 GLOBAL_VARS;顺序不能反;类型要先定义

一句话 :把全局变量集中写在一个头文件里,用一个 GLOBAL_VARS 开关控制"定义"还是"声明",只在一个 .c 文件里定义它,其他文件自动获得 extern 声明。这是嵌入式 C 项目里管理全局变量的标准做法。

相关推荐
LCG米2 小时前
STM32串口接收卡死根因排查:ORE溢出标志陷阱、错误标志清除顺序与中断恢复实测
stm32·单片机·嵌入式硬件·串口·嵌入式
创界工坊工作室3 小时前
38-基于单片机的老人健康监测系统
单片机·嵌入式硬件·51单片机·proteus
三佛科技-134163842125 小时前
20W~200W副边反馈SSR隔离方案HN1562+碳化硅MOS(含光耦817,431)
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
恒锐丰科技林技术员6 小时前
EG2103 高压 MOS/IGBT 驱动芯片解析:半桥驱动的高性价比之选
经验分享·嵌入式硬件·硬件工程
_Ningye9 小时前
STM32 — 11.5 硬件SPI读写W25Q64
stm32·单片机·嵌入式硬件
inrichtt9 小时前
第九课时:STM32 SPI实战:按键消抖与FLASH断电保护
stm32·单片机·嵌入式硬件
尼喃11 小时前
两节两串锂电池保护板原理图+充电电路二合一电路图
嵌入式硬件
深圳市恒锐丰科技杨生11 小时前
SS6208L 12V 集成 MOS 半桥功率芯片|LeadPower 率能半导体
嵌入式硬件·硬件工程
女神下凡12 小时前
芯参谋(61):PMSM永磁同步电机驱动电路设计指南
arm开发·单片机·嵌入式硬件·硬件工程·设计规范
HRTOS12 小时前
HRTOS 4.0 中断嵌套注意事项:为什么不建议在嵌套中断中调用唤醒任务 API?
单片机·嵌入式硬件·51单片机