你有很多 .c 和 .h 文件,全局变量需要在多个 .c 文件之间共享。使用 GLOBAL_VARS 开关的目的是:让变量只被定义一次,其他文件只做声明。
下面是完整的操作步骤。
一、总体原则
-
所有全局变量集中定义在一个头文件里 (例如
global_vars.h)。 -
只在一个
.c文件里定义GLOBAL_VARS,这个文件负责真正分配内存。 -
其他所有
.c文件只包含这个头文件 ,自动获得extern声明。 -
不要在多个头文件里定义同一个变量,否则会重复定义。
二、具体操作步骤
第 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 项目里管理全局变量的标准做法。