关于extern和模块化编程全局变量、函数、对象不同源文件之间的使用说明
extern的使用
在C语言中,extern
关键字用于在多个源文件之间声明全局变量、函数或对象,实现模块间的交互和数据共享。即告诉编译器该变量或函数是在其他源文件中定义的,并且可以在当前源文件中使用。而局部函数的作用域限定在其定义所在的作用域内,无法被其他源文件直接访问或调用。因此,extern
关键字不能用于共享局部函数。
对于变量
下面是一个使用extern共享全局变量
的示例:
main.c
#include <stdio.h>
extern int globalVar; // 声明全局变量
//等同extern globalVar; keil里这样写不会报错 但没有反应直接
但是keil里
int main() {
globalVar = 42; // 在其他模块中修改全局变量的值
printf("Global variable: %d\n", globalVar);
return 0;
}
other.c
int globalVar = 10; // 定义全局变量
void printMessage() {
printf("Hello, extern!\n");
}
在这个示例中,main.c
中使用extern
声明了一个全局变量globalVar
,该全局变量在other.c
中定义。main.c
可以通过extern
声明来访问和使用globalVar
的值。
注意,在编译时,需要将main.c
和other.c
都包含在编译过程中,以便正确链接和使用extern
声明的全局变量或函数。
错题记录
这样虽然编译通过 但无反应。如果这样就正常:
对于函数
下面是一个使用extern共享全局函数
的示例:
module1.c
void sharedFunction() // 在module1.c中定义共享函数
{
/*-------------*/
}
module2.c
extern void sharedFunction(); // 在module2.c中使用extern声明共享函数
//等同extern sharedFunction(); keil里这样写不报错 但没效果
int main() {
sharedFunction(); // 可以在其他模块中调用共享函数
return 0;
}
宏定义常量和const常量区别
预处理
预处理是编译过程的一个阶段,在源代码被编译之前进行。在预处理阶段,预处理器会根据预处理指令处理源代码,包括宏替换、条件编译等。
预处理头文件
文件包含是 C 预处理程序的另一个重要功能。文件包含命令的一般形式为
#include"文件名" 或 #include<文件名>
文件包含命令的功能是把指定的文件插入谈该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。因为有些公用的符号常量或宏定义等可单单独组成一个文件,在其他文件的开头用包含命令包含该文件即可使用。这样,可避免在每 个文件开头都去书写那些公用量,从而节省时 间,并减少出错。
宏定义
宏定义就是在预处理阶段被展开成它们的值,并在编译时被硬编码到每个使用它们的地方,其作用域为宏定义命令起到源程序结束。这意味着它们的值在编译时是固定的,不存在需要共享的定义,所以 使用extern
关键字进行共享宏定义是不必要的。另外,宏定义其值是不可修改的。
宏定义的常量没有类型,它是在编译前即预编译阶段进行字符替换,用#define定义,如下:
#define Pi 3.1415926
const常量
const定义常量则是由编译器在编译时处理的,而不是在预处理阶段。编译器会在编译过程中对常量进行处理,并将它们的值硬编码到生成的目标代码中。它是在运行时进行替换,其值在运行期间恒定不变,并且在编译时会进行严格的类型检验,同时系统也会为常量分配内存。const
关键字用于声明只读的常量,一旦被赋值,其值就不能再被改变,但是如果非要修改我们可以使用指针来修改其值。常量使用关键字const定义,如下:
const float PI = 3.14159;
C++语言可以用const 来定义常量,也可以用#define来定义宏常量。但是两者的区别在于:
1. const 常量在运行时进行替换,宏常量则是在预编译截断进行替换
2. const 常量有数据类型,而宏常量没有数据类型;
const和宏定义的应用
如果需要在不同的源文件中共享const常量的值(宏定义也是),可以将常量的定义放在一个头文件中,并在需要使用它的源文件中包含该头文件。这样可以确保不同的源文件使用的是相同的常量值。我们通常使用引用其头文件的方法,例如:
constants.h
#define PI 3.14159 // 在constants.h中定义常量或宏
extern const int MAX_SIZE; // 使用extern声明共享常量
int add(int a, int b); //函数的声明
constants.c
#include "constants.h"
const int maxNum = 100; // 在constants.c中定义共享常量
int add(int a, int b) {
return a + b;
}
main.c
#include <stdio.h>
#include "constants.h"
int totalNum;
int main() {
printf("Max value: %d\n", PI); // 可以在其他模块中使用共享常量或宏
printf("Max size: %d\n", maxNum); // 可以在其他模块中使用共享常量或宏
totalNum=add(PI+maxNum); //引用共享函数
return 0;
}
/* ------------------------------分 隔 符------------------------------*/
对于全局变量,如果在对应的.h文件声明了,我们在其他源文件中如果要引用该变量,可以直接引用其头文件,然后直接使用。例如:
module1.h
extern int globalVar; // 声明全局变量
int add(int a, int b); //函数的声明
module1.c
#include "module1.h"
int globalVar = 42; // 定义全局变量
int add(int a, int b) {
return a + b;
}
module2.c
#include "module1.h"
int globalVar = 65; // 修改全局变量值
使用头文件的好处是,可以将共享的声明集中在一个地方,并在需要的源文件中引用,提高代码的可读性和可维护性。此外,头文件还可以包含其他模块所需的函数原型、结构体定义、宏定义等,方便模块之间的交互和共享。
总结来说,虽然在使用extern
声明其他源文件的全局变量时并不一定需要引入头文件,但为了确保声明的一致性和代码的可维护性,通常建议使用头文件来声明和定义共享的实体。