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 项目里管理全局变量的标准做法。

相关推荐
俊基科技5 小时前
喇叭 95dB、麦克风贴到 1cm,回音还能消干净?拆解 A-29P 的消回音设计
嵌入式硬件·硬件开发·ai降噪·回音消除·拾音降噪
2401_862880825 小时前
LCD 显示与触控屏驱动开发
驱动开发·嵌入式硬件
糖糖单片机设计6 小时前
基于STM32的指纹刷卡智能门禁系统设计(RC522 + AS608 + 蓝牙远程开锁 + 断电记忆)
人工智能·stm32·嵌入式硬件·51单片机·语音识别
HRTOS7 小时前
HRTOS Shell继续完善:优化printf缓冲区与字符串越界保护,国庆后随新版本更新
运维·单片机·嵌入式硬件·算法·51单片机
YYRAN_ZZU7 小时前
GMSL开发加解串配置介绍
linux·嵌入式硬件
zhaoshuzhaoshu7 小时前
回调函数详解 —— 以 STM32 平台为例
嵌入式硬件
ccbw167 小时前
CH592 蓝牙 MCU 集成方案与低功耗设计要点
单片机·嵌入式硬件
I'm a winner8 小时前
《AI 赋能嵌入式开发:从 0 到全栈工程师》模块1|第3课时
人工智能·嵌入式硬件
数字新视界8 小时前
2026机房动环监控选型指南:场景规模需求、市场发展占有率和竞争梯队报告解析
大数据·运维·网络·人工智能·嵌入式硬件·机房动环监控·机房环境安全