头文件中定义static静态变量的利弊

转自:http://blog.csdn.net/caiqinghua0201/article/details/6076333

对于存储位置:静态变量的效果和全局变量的效果是一样的,他们都保存在全局数据区。频繁使用其实效率更高,如果定义的静态变量不用,那就会浪费内存,因为全局数据区生命周期是从程序运行到结束。

局部变量:每次栈上都要为局部变量分配空间,因此局部变量太多会影响效率,但是其回收方便,对于稳定性要求高的系统来说,这样的操作会更可靠。

全局变量(包含静态变量):其空间会在程序启动时分配好,因此不用不停的分配,频繁使用更好(当然多线程下要同步则会更复杂)。另一点就是它会实实在在的一直占用内存,如果需要太大的内存,同时又不会频繁使用,则建议使用堆变量(特别是嵌入式下内存往往不够用)

堆变量:可动态回收的内存,这样可以实现内存使用效率最大化,但是内存的分配和管理是需要付出时间的代价,而且使用要比较谨慎,否则容易内存泄露,管理很困难。

函数内的静态变量,说明只有这个函数才用到。只暴露给需要用的人,是一个编码的好习惯。

问:

一般来说,静态全局变量只应该定义在实现文件中,但有时由于一些特殊的目的,也可能定义在头文件中。比如在有些标准库的实现中,就用这种方法来初始化标准流cin, cout,或者在在tr1库中,也用这种方法来定义占位符。每一个包含该头文件的实现文件中都拥有该变量的一份拷贝,这些变量放在运行体的data段或者bss段。
比如下面这个变量定义在一个头文件中:
static int data[1024*1024];
我把这个文件同时包含在几个cpp文件中,按我的理解,这个程序占用的内存应该显著增大,但是,从实际运行结果来看,并没有变化,生成的exe文件大小也没有变化,这是因为延迟加载呢,还是被编译器优化掉了?有没有明白的达人解释一下。

答:
这不是编译器的问题,而是OS的virtual memeory管理机制导致的。数据在实际使用之前,是不会占用内存的——缺页异常处理程序会为数据分配需要的内存。

要理解static,就必须要先理解另一个与之相对的关键字,很多人可能都还不知道有这个关键字,那就是auto,其实我们通常声明的不用static修饰的变量,都是auto的,因为它是默认的,就象short和long总是默认为int一样;我们通常声明一个变量:
int a;
string s;
其实就是:
auto int a;
auto string s;
而static变量的声明是:
static int a;
static string s;
这样似乎可以更有利于理解auto和static是一对成对的关键字吧,就像private,protected,public一样;
对于static的不理解,其实就是对于auto的不理解,因为它是更一般的;有的东西你天天在用,但未必就代表你真正了解它;auto的含义是由程序自动控制变量的生存周期,通常指的就是变量在进入其作用域的时候被分配,离开其作用域的时候被释放;而static就是不auto,变量在程序初始化时被分配,直到程序退出前才被释放;也就是static是按照程序的生命周期来分配释放变量的,而不是变量自己的生命周期;所以,像这样的例子:
void func()
{
int a;
static int b;
}
每一次调用该函数,变量a都是新的,因为它是在进入函数体的时候被分配,退出函数体的时候被释放,所以多个线程调用该函数,都会拥有各自独立的变量a,因为它总是要被重新分配的;而变量b不管你是否使用该函数,在程序初始化时就被分配的了,或者在第一次执行到它的声明的时候分配(不同的编译器可能不同),所以多个线程调用该函数的时候,总是访问同一个变量b,这也是在多线程编程中必须注意的!
static的全部用法:
1.类的静态成员:
class A
{
private:
static int s_;
};
在cpp中必须对它进行初始化:
int A::s_ = 0;// 注意,这里没有static的修饰!
类的静态成员是该类所有实例的共用成员,也就是在该类的范畴内是个全局变量,也可以理解为是一个名为A::s_的全局变量,只不过它是带有类安全属性的;道理很简单,因为它是在程序初始化的时候分配的,所以只分配一次,所以就是共用的;
类的静态成员必须初始化,道理也是一样的,因为它是在程序初始化的时候分配的,所以必须有初始化,类中只是声明,在cpp中才是初始化,你可以在初始化的代码上放个断点,在程序执行main的第一条语句之前就会先走到那;如果你的静态成员是个类,那么就会调用到它的构造函数;
2.类的静态函数:
class A
{
private:
static void func(int );
};
实现的时候也不需要static的修饰,因为static是声明性关键字;
类的静态函数是在该类的范畴内的全局函数,不能访问类的私有成员,只能访问类的静态成员,不需要类的实例即可调用;实际上,它就是增加了类的访问权限的全局函数:void A::func(int);
静态成员函数可以继承和覆盖,但无法是虚函数;
3.只在cpp内有效的全局变量:
在cpp文件的全局范围内声明:
static int g_ = 0;
这个变量的含义是在该cpp内有效,但是其他的cpp文件不能访问这个变量;如果有两个cpp文件声明了同名的全局静态变量,那么他们实际上是独立的两个变量;
如果不使用static声明全局变量:
int g_ = 0;
那么将无法保证这个变量不被别的cpp共享,也无法保证一定能被别的cpp共享,因为要让多个cpp共享一个全局变量,应将它声明为extern(外部)的;也有可能编译会报告变量被重复定义;总之不建议这样的写法,不明确这个全局变量的用法;
如果在一个头文件中声明:
static int g_vaule = 0;
那么会为每个包含该头文件的cpp都创建一个全局变量,但他们都是独立的;所以也不建议这样的写法,一样不明确需要怎样使用这个变量,因为只是创建了一组同名而不同作用域的变量。
这里顺便说一下如何声明所有cpp可共享的全局变量,在头文件里声明为extern的:
extern int g_; // 注意,不要初始化值!
然后在其中任何一个包含该头文件的cpp中初始化(一次)就好:
int g_ = 0; // 初始化一样不要extern修饰,因为extern也是声明性关键字;
然后所有包含该头文件的cpp文件都可以用g_这个名字访问相同的一个变量;
4.只在cpp内有效的全局函数:
在cpp内声明: static void func();
函数的实现不需要static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;道理和如果不使用static会引起的问题和第3点一样;不要在头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则在cpp内部声明需要加上static修饰;在C语言中这点由为重要!

<think>好的,用户的问题是关于在C语言项目中如何在头文件中声明变量,并在源文件中进行初始化的方法。我需要结合提供的引用资料来解答这个问题。 首先,根据引用[1],头文件(.h)通常用于声明变量、函数等,而源文件(.c)用于定义。声明告诉编译器存在这个变量,而定义则分配内存空间。用户可能在头文件中声明变量,然后在源文件中初始化,但需要注意重复定义的问题。 接下来,引用[2]提到在头文件中初始化静态变量会导致每个包含该头文件的源文件都有一个独立的静态变量实例,这可能引发问题。这说明静态变量头文件中初始化的风险,需要避免。 引用[5]指出,如果在头文件定义变量,多个源文件包含该头文件会导致重复定义错误。这是因为每个源文件都会有自己的变量实例,链接时会冲突。因此,正确的做法是在头文件中使用extern声明变量,然后在源文件中定义。 用户的问题是如何正确在头文件声明变量并在源文件初始化。正确的步骤应该是: 1. 在头文件用extern声明变量,如extern int var; 这样只是声明,不分配内存。 2. 在源文件中定义并初始化变量,如int var = 0; 这样确保只有一个定义。 3. 如果变量是静态的,需注意作用域,只能在当前源文件使用,不能在头文件中声明,否则每个包含该头文件的源文件都会有独立的副本,如引用[2]中的例子。 可能用户遇到的问题是在头文件中直接定义变量导致重复定义,或者错误地初始化静态变量。需要强调正确使用extern和避免在头文件定义变量。 另外,可能需要提到头文件保护(#ifndef)来防止重复包含,但这可能不是用户当前问题的重点。 总结回答结构:先解释声明与定义的区别,然后给出正确步骤,指出常见错误,如静态变量的处理,最后给出示例代码。</think>### 如何在C语言头文件中声明变量并在源文件中初始化? 在C语言项目中,正确地在头文件中声明变量并在源文件中初始化的方法如下: --- #### **1. 头文件中声明变量(使用`extern`关键字)** 在头文件(如`global.h`)中,使用`extern`关键字声明变量,表示该变量在其他源文件中定义。例如: ```c // global.h #ifndef GLOBAL_H #define GLOBAL_H extern int global_var; // 声明变量,不分配内存 extern const char* str; // 声明常量字符串 #endif ``` - **`extern`的作用**:告诉编译器变量在外部文件中定义,避免重复分配内存[^1][^5]。 - **头文件保护**:`#ifndef`防止头文件被多次包含。 --- #### **2. 源文件中定义并初始化变量** 在某个源文件(如`main.c`)中定义并初始化变量: ```c // main.c #include "global.h" int global_var = 42; // 定义并初始化全局变量 const char* str = "Hello"; // 定义并初始化常量字符串 ``` - **定义与初始化的区别**:定义会分配内存,而`extern`声明仅引用已存在的变量[^1]。 - **单一定义规则**:变量只能在**一个源文件**中定义,否则会导致链接错误。 --- #### **3. 其他源文件引用变量** 其他文件(如`func.c`)只需包含头文件即可使用变量: ```c // func.c #include "global.h" void print_var() { printf("global_var = %d\n", global_var); // 直接使用extern声明的变量 } ``` --- ### **常见错误与注意事项** 1. **避免在头文件定义变量** 若在头文件中直接定义变量(如`int var = 0;`),多个源文件包含该头文件时会导致重复定义错误。 2. **静态变量(`static`)的特殊性** 静态变量仅在当前文件内有效。若在头文件定义`static`变量,每个包含该头文件的源文件会生成独立的副本,可能导致非预期行为[^2]: ```c // 错误示例(在头文件定义静态变量static int local_var = 0; // 每个包含此头文件的.c文件会有自己的local_var ``` 3. **常量与宏定义的替代方案** 对于常量,可使用`#define`或`const`+`extern`组合: ```c // 方法1:使用宏 #define MAX_SIZE 100 // 方法2:使用extern const(需在源文件中定义) extern const int MAX_SIZE; ``` --- ### **代码示例** ```c // global.h #ifndef GLOBAL_H #define GLOBAL_H extern int counter; extern const double PI; #endif ``` ```c // global.c #include "global.h" int counter = 0; // 定义并初始化全局变量 const double PI = 3.1415; // 定义常量 ``` --- ### **相关问题** 1. 如何避免头文件重复包含导致的编译错误? 2. 静态全局变量(`static`)和非静态全局变量(`extern`)的作用域有何区别? 3. 在C语言中,`const`变量和宏定义的优缺点分别是什么? : 在源文件(&.c)和头文件(&.h)中声明和定义的区别——C语言 : C语言非静态方法给静态变量赋值 : 工程中符号重复定义的错误原因
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值