全局变量、局部变量、extern

来源:互联网 发布:^ c语言 编辑:程序博客网 时间:2024/05/21 20:22

全局对象是一个运行时刻实体,它在程序的整个执行过程中都存在。全局对象占据的存储区的生命期(lifetime) 从程序启动开始在程序终止时结束。
    全局对象和非inline 全局函数在一个程序内只能被定义一次。而只要给出的定义完全相同即可inline 函数可以在一个程序中被定义多次。这要求全局对象和函数或者只有一个定义,或者在一个程序中有多个完全相同的定义,这样的要求被称为一次定义法则ODR(one definition rule)。
    在全局域中定义的对象,如果没有指定显式的初始值,则该存储区被初始化为0。so下列var1和var2有相同的初始值0:
 int var1 = 0 ;
 int var2 ;
    关键字extern 为声明但不定义一个对象提供了一种方法。实际上,它类似于函数声明承诺了该对象会在其他地方被定义:或者在此文本文件中的其他地方,或者在程序的其他文本文件中。
    extern 声明不会引起内存被分配,它可以在同一文件中或同一程序的不同文件中出现多次。典型情况下,全局对象的声明只在公共的头文件中出现一次,当一个程序文件需要引用这个全局对象时,它可以包含这个头文件。
    既指定了关键字extern 又指定了一个显式初始值的全局对象声明将被视为该对象的定义。如 extern const double pi = 3.1416 ;
    在多个文件中声明对象或函数的一个可能问题是:在不同文件中的声明可能会随时间而不同或改变。C++为检查不同文件中函数声明的差异提供了一些支持。
    在C++中有一种机制,通过它可以把函数参数的类型和数目编码在函数名中,该机制叫做类型安全链接type-safe-linkage。类型安全链接可用来帮助捕捉不同文件中函数声明不匹配的情况。如:
 // ---- token.C 中 ----
 int addToken( unsigned char tok ) { /* ... */ }
 // ---- lex.C 中 ----
 extern int addToken( char );
    类型安全链接机制为文件之间的函数调用提供了类型检查手段。它对支持重载函数也是必需的。
    不同文件中出现的同一对象或函数声明的其他类型不匹配情况在编译或链接时可能不会被捕捉到因为编译器一次只能处理一个文件它不能很容易地检查到文件之间的类型违例这些类型违例可能是程序严重错误的根源例如文件之间错误的对象声明或函数返问类型就不能被检测出来这样的错误只能在运行时刻异常或程序的错误输出中才能被揭示出来
 // token.C 中
 unsigned char lastTok = 0;
 unsigned char peekTok() { /* ... */ }
 // in lex.C
 extern char lastTok; // 最后一个 token
 extern char peekTok(); // 查看 token
    使用头文件是防止此类错误的基本法则。头文件为所有extern 对象声明、函数声明以及inline 函数定义提供了一个集中的位置,这被称作声明的局部化(localization)。
  头文件提供了两个安全保证:
    第一保证所有文件都包含同一个全局对象或函数的同一份声明;
    第二如果需要修改声明则只需改变一个头文件。
  设计头文件有一些要注意的地方:
    第一、头文件提供的声明逻辑上应该属于一个组。编译头文件也需要时间,如果头文件过大或分散的元素太多,程序员可能会不愿意因为包含它而增加编译时间开销为降低编译时间开销。有些C++实现提供了预编译头文件支持。如果应用程序有很大的头文件,则使用预编译头文件而不是普通头文件,可以大大降低应用程序的编译时间。
    第二个考虑是头文件不应该含有非inline 函数或对象的定义。
    符号常量和inline 函数可以被定义多次。
 
    有三种局部对象:
  自动对象(automatic object)、 寄存器对象(register object) 以及局部静态对象(local static object)。区分这些对象的是对象所在存储区的属性和生命期。自动对象所在存储区从声明它的函数被调用时开始一直到该函数结束为止,寄存器对象是一种自动对象它支持对其值的快速存
取,局部静态对象的存储区在该程序的整个执行期间一直存在。

  自动对象的存储分配发生在定义它的函数被调用时,分配的存储区来自程序的运行栈。未初始化的自动对象包含一个随机的位模式,是该存储区上次被使用的结果(所以使用未初始化的自动对象会导致未定义行为)。
  在函数结束时,它的活动记录被从运行栈中弹出。与该自动对象相关联的存储区被真正释放,对象的生命期在函数结束时结束,它包含的任何值都被抛弃。
  自动对象的地址不应该被用作函数的返回值,因为函数一旦结束了,该地址就指向一个无效的存储区。
  在函数中频繁被使用的自动变量可以用register 声明。如果可能的话编译器会把该对象装载到机器的寄存器中,如果不能够的话,则对象仍位于内存中。例如循环语句中的数组索引和指针。
  如果所选择的变量被频繁使用,则寄存器变量可以提高函数的执行速度。关键字register 对编译器来说只是一个建议。
 
  静态局部变量的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。
  未经初始化的静态局部对象会被程序自动初始化为0 。(相反,自动对象的值会是任意的,除非它被显式初始化)