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

本文详细介绍了C/C++中extern、static关键字的用法,以及如何防止头文件重复包含。extern用于在其他文件中引用全局变量或函数,static用于限制变量的作用域或生命周期。此外,还讨论了C++中extern "C"的用法,以解决C++与C混合编程时的名称匹配问题。同时,文章讲解了static在全局变量、局部变量和函数上的不同影响,以及与普通变量和函数的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


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

test.htest.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.ctest.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.ctest1.c--diff

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

extern char add1[]; 

void test1()          
{                         
   printf(add1);           
}  

×××

×××

×××

×××

×××

×××

×××

test2.ctest2.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修饰全局变量的作用域 只能是本身的编译单元。

在VSCode中使用C语言编程时,如果遇到无法使用关键字`extern`的问题,可以尝试以下几种解决方法: 1. **检查文件扩展名**: 确保你的文件扩展名是`.c`而不是其他扩展名。VSCode有时会因为文件扩展名不正确而导致语法高亮和关键字识别问题。 2. **安装C/C++扩展**: 确保你已经安装了微软官方的C/C++扩展。这个扩展提供了C/C++语言的支持,包括语法高亮、代码补全和调试功能。 3. **配置c_cpp_properties.json**: 在VSCode中,`c_cpp_properties.json`文件用于配置C/C++编译器的路径和包含目录。确保这个文件中的配置正确,特别是包含路径(`includePath`)和宏定义(`defines`)部分。 ```json { "configurations": [ { "name": "Win32", "includePath": [ "${workspaceFolder}/**" ], "defines": [ "_DEBUG", "UNICODE" ], "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "gcc-x64" } ], "version": 4 } ``` 4. **检查代码文件**: 确保你的代码文件中没有语法错误或其他问题,这些问题可能会导致VSCode无法正确解析`extern`关键字。 5. **重启VSCode**: 有时,简单的重启VSCode可以解决一些意想不到的问题。 6. **清理和重新生成项目**: 如果你使用的是构建系统(如Makefile),尝试清理项目并重新生成,以确保所有文件都被正确编译。 通过以上步骤,你应该能够在VSCode中正常使用`extern`关键字。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值