C语言const,extern,static 浅析

早上过来抢劫了师弟们的代码,顺便了解一下单片机里面程序运行的过称,然后自己跑回来装上IAR稍稍看了一下。代码写的还是正规正局,没有全部堆积在一个文件里面,而是为每个外设归纳了文件,注释也蛮到位,增加了程序的可读性。

读的过程中看到头文件里面的extern,貌似有些印象,然后就google一下,看了起用法,顺便牵连起了const和static,记录粘贴于此,供以后回顾之用。


extern

外部变量或函数的作用域从声明它的地方开始,到其所在的文件的结尾结束。另一方面,如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性地使用关键字extern。

外部变量的声明和定义的严格区分是很重要的。变量声明应于说明变量的属性(主要是变量的类型),而变量定义除此以外还将引起存储器的分配。

extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。另外,extern也可用来进行链接指定。


下面是一个拷贝的例子

在一个项目中必须保证函数、变量、枚举等在所有的源文件中保持一致,除非你指定定义为局部的。首先来一个例子:


1
2
3
4
5
6
7
//file1.c:
int x=1;
int f(){ do something here}
//file2.c:
extern int x;
int f();
void g(){x=f();}

在file2.c中g()使用的x和f()是定义在file1.c中的。extern关键字表明file2.c中x,仅仅是一个变量的声明,其并不是在定义变量x,并未为x分配内存空间。变量x在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。但是可以声明多次,且声明必须保证类型一致,如:


1
2
3
4
5
6
7
8
9
//file1.c:
int x=1;
int b=1;
extern c;
//file2.c:
int x; // x equals to default of int type 0
int f();
extern double b;
extern int c;

在这段代码中存在着这样的三个错误:

  1. x被定义了两次
  2. b两次被声明为不同的类型
  3. c被声明了两次,但却没有定义

回到extern关键字,extern是C/C++语言中表明函数全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

const
 为什么使用const?
 采用符号常量写出的代码更容易维护;指针常常是边读边移动,而不是边写边移动;许多函数参数是只读不写的。const最常见用途是作为数组的界和switch分情况标号(也可以用枚举符代替) 


用法1:常量
 取代了C中的宏定义,声明时必须进行初始化。const限制了常量的使用方式,并没有描述常量应该如何分配。如果编译器知道了某const的所有使用,它 甚至可以不为该const分配空间。最简单的常见情况就是常量的值在编译时已知,而且不需要分配存储。―《C++ Program Language》
 用const声明的变量虽然增加了分配空间,但是可以保证类型安全。
 C标准中,const定义的常量是全局的,C++中视声明位置而定。
 
用法2:指针和常量
 使用指针时涉及到两个对象:该指针本身和被它所指的对象。将一个指针的声明用const“预先固定”将使那个对象而不是使这个指针成为常量。要将指针本身而不是被指对象声明为常量,必须使用声明运算符*const。
 所以出现在 * 之前的const是作为基础类型的一部分:
 char *const cp; //到char的const指针
 char const *pc1; //到const char的指针
 const char *pc2; //到const char的指针(后两个声明是等同的)
 从右向左读的记忆方式:
 cp is a const pointer to char.
 pc2 is a pointer to const char.
 
用法3:const修饰函数传入参数
 将函数传入参数声明为const,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值。同理,将指针参数声明为const,函数将不修改由这个参数所指的对象。
 通常修饰指针参数和引用参数:
 void Fun( const A *in); //修饰指针型传入参数
 void Fun(const A &in); //修饰引用型传入参数
 
用法4:修饰函数返回值
 可以阻止用户修改返回值。返回值也要相应的付给一个常量或常指针。
 
用法5:const修饰成员函数
 const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数;
 const对象的成员是不能修改的,而通过指针维护的对象确实可以修改的;
 const成员函数不可以修改对象的数据,不管对象是否具有const性质。编译时以是否修改成员数据为依据进行检查。
 


static
 静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为0,使用时可以改变其值。
 静态变量或静态函数只有本文件内的代码才能访问它,它的名字在其它文件中不可见。
 用法1:函数内部声明的static变量,可作为对象间的一种通信机制
 如果一局部变量被声明为static,那么将只有唯一的一个静态分配的对象,它被用于在该函数的所有调用中表示这个变量。这个对象将只在执行线程第一次到达它的定义使初始化。
 用法2:局部静态对象
 对于局部静态对象,构造函数是在控制线程第一次通过该对象的定义时调用。在程序结束时,局部静态对象的析构函数将按照他们被构造的相反顺序逐一调用,没有规定确切时间。
 用法3:静态成员和静态成员函数
 如果一个变量是类的一部分,但却不是该类的各个对象的一部分,它就被成为是一个static静态成员。一个static成员只有唯一的一份副本,而不像常 规的非static成员那样在每个对象里各有一份副本。同理,一个需要访问类成员,而不需要针对特定对象去调用的函数,也被称为一个static成员函 数。
 类的静态成员函数只能访问类的静态成员(变量或函数)。
 
一般情况下,对于局部变量是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时便结束了。但是如果用static进行修饰的话,该变量便存放在静态数据区,其生命周期一直持续到整个程序执行结束。但是在这里要注意的是,虽然用static对局部变量进行修饰过后,其生命周期以及存储空间发生了变化,但是其作用域并没有改变,其仍然是一个局部变量,作用域仅限于该语句块。

在用static修饰局部变量后,该变量只在初次运行时进行初始化工作,且只进行一次。

如:

 
 
  1. #include<stdio.h>
  2. void fun()
  3. {
  4. static int a=1; a++;
  5. printf("%d\n",a);
  6. }
  7. int main(void)
  8. {
  9. fun();
  10. fun();
  11. return 0;
  12. }

程序执行结果为: 2 3

说明在第二次调用fun()函数时,a的值为2,并且没有进行初始化赋值,直接进行自增运算,所以得到的结果为3.

对于静态局部变量如果没有进行初始化的话,对于整形变量系统会自动对其赋值为0,对于字符数组,会自动赋值为'\0'.

C和指针中对static关键字总结

当static用于函数定义是,或用于代码块之外的变量声明时,static关键字用于修改标示符的链接属性,从external改为internal,但标示符的存储类型和作用域不受影响。用这种方式声明函数或变量只能在声明它们的源文件中访问。

当它用于代码块内部的变量声明时,static关键字用于修改变量的存储类型,从自动变量修改为静态变量,但变量的链接属性和作用域不受影响。用这种方式声明的变量的程序执行之前创建,并在程序的整个执行期间一直存在,并不是每次在代码块开始执行时创建,在代码块执行完毕后销毁。


 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值