C/C++ extern-static-全局变量-局部变量-宏定义等

1. 如何防止头文件被重复包含(#include)
-- 可以使用条件编译,所有头文件都标记:
    #ifndef _HEADERNAME_H
    #define _HEADERNAME_H
         #include ...
    #endif
    当头文件第一次被包含时,它被正常处理,符号_HEADERNAME_H被定义为1,头文件被再次包含,通过条件编译,它的内容被忽略;符号_HEADERNAME_H按照被包含头文件的文件名进行取名,以避免由于其他头文件使用相同的符号而引起的冲突。


2. 如何放防止变量被重复定义

test.h test.h--diff
#ifndef _TEST_H_                        
#define _TEST_H_ // 防止test.h被重复包含            
char add1[] = "zly";   
char add2[] = "zyj";   
int i = 10;                          
void test1();                            
void test2();                            
#endif                                
×××
×××
extern char add1[];
extern char add2[];
extern int i;
×××
×××
×××
test.c test.c--diff

#include <stdio.h>  
#include "test.h"   

extern i;  
extern void test1();  
extern void test2(); 

int main()  
{                                
   test1();                            
   printf("ok/n");                        
   test2();                            
   printf("%d/n",i);                        
   return 0;                            
}                                

 ×××
 ×××

int i = 10; 
char add1[] = "zly"
char add1[] = "zyj"
extern void test1();
extern void test2();
 ×××
 ×
 ×
 ×
 ×
 ×
 ×
 ×××

test1.c test1.c--diff

#include <stdio.h> 
#include "test.h"   

extern char add1[]; 

void test1()          
{                         
   printf(add1);           
}  

×××

×××

×××

×××

×××

×××

×××

test2.c test2.c--diff

#include <stdio.h>     
#include "test.h"      

extern char add2[];  
extern i;                      

void test2()                    
{                             
   printf(add2);                    
   for (; i > 0; i--)            
       printf("%d-", i);          
}  

×××

×××

×××

×××

×××

×××

×××

×××

×××

      .c文件中只要包含了test.h就会独立的解释,然后每个.c文件生成独立的标示符。在编译器链接时,就会将工程中所有的符号整合在一起,由于文件中有重名变量,于是就出现了重复定义的错误;
解决方法:
      在.h头文件中声明变量并加上extern,不要对变量初始化,在使用该全局变量的.c文件中包含该头文件,并在.c文件中定义变量。编译器会为.c生成目标文件,然后链接时,如果该.c文件使用了全局变量,链接器就会链接到定义变量的.c文件.

3. 变量的声明有两种情况,3.1是"定义性声明(defining declaration),3.2是"引用性声明(referncingdeclaration)";extern只作引用声明,不作定义;
   3.1.一种是需要建立存储空间的(定义、声明)。例如:int a在声明的时候就已经建立了存储空间;
   3.2.另一种是不需要建立存储空间的(声明)。例如:extern int a其中变量a是在别的文件中定义的;4. 链接指示符:extern:
   4.1:修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。

file1.c
---------------------
int a=1;

file2.c
---------------------
#include<stdio.h>

extern int a;

int main(void)

{
  printf("%d\",a);
  return 0;
}


    结果:1;但是如果在file1.c中把int a=1改为static int a=1;


   4.2:C++中extern还可以用来访问C程序的变量和函数用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同,用此来解决名字匹配的问题作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在symbol库中的名字与C语言的不同。例如,假设某个函数的原型为:
    void foo(int x, int y); 
    该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。 为了实现C和C++的混合编程,C++提供了C链接交换指定符号extern "C"来解决名字匹配问题,函数声明前加上extern "C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。
  a.被extern "C"限定的函数或变量首先是extern类型的
  b.extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用
  c.被extern "C"修饰的变量和函数是按照C语言方式编译和连接的

  4.2.1 C++ 调用C
  方法a.
#ifdef __cplusplus  
extern "C"  
#endif  
 void DeleteStack(Stack stack); //c函数
 void PrintStack(Stack stack); /c函数

  方法b. 
extern "C" {  
#include "Stack.h";  /c函数头文件
}  

  4.2.2 C 调用C++
  C++ 头文件加上:
extern "C" {
int testmain();
}
  C文件include C++头文件的方式调用
  C文件不支持extern "C"
5. static:static可以用来修饰局部变量,全局变量以及函数。static有3个作用:首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0。C++中static还具有其它功能,如果在C++中对类中的某个函数用static进行修饰,则表示该函数属于一个类而不是属于此类的任何特定对象.如果对类中的某个变量进行static修饰,表示该变量为类以及其所有的对象所有.可以通过类和对象去调用。对于静态成员函数,只能访问静态成员函数和静态成员变量,不能访问非静态成员函数或者变量。
  a.隐藏:同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。如2个源文件a.c和main.

-----------------------------------
a.c:
char a = 'A'; // global variable
void msg() 
{
    printf("Hello\n"); 
}

-----------------------------------
main.c:
int main(void)
{    
    extern char a;    // extern variable must be declared before use
    printf("%c ", a);
    (void)msg();
    return 0;
}
-----------------------------------


结果:A Hello
分析:a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。
  b.保持变量内容的持久:存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围.
  c.默认初始化为0:其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00.


6. 常见疑问:
  6.1 static全局变量与普通的全局变量有什么区别 ?
      --全局变量前加static修饰,就变成了静态全局变量,static全局变量只在当前源文件内有效,而普通全局变量在整个程序的源文件都可以使用;全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。static全局变量只初使化一次,防止在其他文件单元中被引用; 

  6.2 static局部变量和普通局部变量有什么区别 ?
      --局部变量加上static变成静态变量之后改变了它的声明周期,全局变量加上static变成静态变量之后改变了它的作用域;static局部变量只被初始化一次,下一次依据上一次结果值; 

  6.3 static函数与普通函数有什么区别?
      --static修饰的函数与普通函数相比作用域不同,static的函数只能在当前文件中使用;对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

  6.4 extern与static:
      --首先extern和static不能同时修饰一个变量;其次static修饰的全局变量声明与定义同时进行;最后,static修饰全局变量的作用域 只能是本身的编译单元。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章