深入理解extern使用方法

一、 extern做变量声明

l  声明externkeyword的全局变量和函数可以使得它们可以跨文件被訪问。

我们一般把全部的全局变量和全局函数的实现都放在一个*.cpp文件中面,然后用一个同名的*.h文件包括全部的函数和变量的声明。如:

/*Demo.h*/
#pragma  once
extern inta;
extern intb;
intadd(inta,intb);
/*Demo.cpp*/
#include "Demo.h" /*这句话写或者不写在本例中都行,只是建议不写*/
/*不写不会出问题,写了有些情况下会出问题,以下有解释*/
int a =10;
int b =20;

int add(intl,intr)
{
      return l +r;
}

假设将Demo.cpp写成了Demo.c,编译器会告诉你说无法解析的外部符号。

由于Demo.c里面的实现会被C编译器处理,然而C++和C编译器在编译函数时存在差异,所以会存在找不到函数的情况。

l  全局函数的声明语句中,keywordextern能够省略,由于全局函数默认是extern类型的。

l 声明和定义

externint a; //属于声明 
externint a = 10; //属于定义,同下

externchar
g_str[]="123456";//这个时候相当于没有extern

假设在一个文件中定义了char g_str[] = "123456";在另外一个文件中必须使用extern char g_str[ ];来声明。不能使用extern char* g_str;来声明。

extern是严格的声明。

且extern char* g_str仅仅是声明的一个全局字符指针。

注:声明能够拷贝n次,可是定义仅仅能定义一次。

二、extern “C”

l  extern "C" 包括双重含义。从字面上就可以得到:首先,被它修饰的目标是“extern”的;其次。被它修饰的目标是“C”的。

被extern "C"限定的函数或变量是extern类型的:

extern是C/C++语言中表明函数和全局变量作用范围(可见性)的keyword,该keyword告诉编译器,其声明的函数和变量能够在本模块或其他模块中使用。

记住。下列语句:

extern int a;

不过一个变量的声明,其并非在定义变量a,并未为a分配内存空间。变量a在全部模块中作为一种全局变量只能被定义一次。否则会出现连接错误。

通常,在模块的头文件里对本模块提供给其他模块引用的函数和全局变量以keywordextern声明。

比如,假设模块B欲引用该模块A中定义的全局变量和函数时仅仅需包括模块A的头文件就可以。这样。模块B中调用模块A中的函数时,在编译阶段,模块B尽管找不到该函数,可是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

与extern相应的keyword是static,被它修饰的全局变量和函数仅仅能在本模块中使用。因此。一个函数或变量仅仅可能被本模块使用时。其不可能被extern “C”修饰。

实现C++与C及其他语言的混合编程:

被extern"C"修饰的变量和函数是依照C语言方式编译和连接的。未加extern “C”则依照声明时的编译方式。

l  extern "C"的惯使用方法

(1)在C++中引用C语言中的函数和变量,在包括C语言头文件(如果为cExample.h)时。需进行下列处理:

extern "C"{

#include "cExample.h"

}

而在C语言的头文件里,对其外部函数仅仅能指定为extern类型,C语言中不支持extern"C"声明,在.c文件里包括了extern"C"时会出现编译语法错误。

(2)在C中引用C++语言中的函数和变量时,C++的头文件需加入extern"C",可是在C语言中不能直接引用声明了extern"C"的该头文件。应该仅将C文件里将C++中定义的extern"C"函数声明为extern类型。

三、 extern 和static

(1)extern表明该变量在别的地方已经定义过了。在这里要使用那个变量。

(2)static 表示静态的变量,分配内存的时候,存储在静态区,不存储在栈上面。

static作用范围是内部连接的关系这和extern有点相反。它和对象本身是分开存储的,extern也是分开存储的。可是extern能够被其它的对象用extern引用,而static不能够,仅仅同意对象本身用它。详细区别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同一时候修饰一个变量。其次。static修饰的全局变量声明与定义同一时候进行。也就是说当你在头文件里使用static声明了全局变量后。它也同一时候被定义了;最后,static修饰全局变量的作用域仅仅能是本身的编译单元,也就是说它的“全局”仅仅对本编译单元有效,其它编译单元则看不到它,如:

/*test1.h*/
#ifndef TEST1H
#define TEST1H
static char g_str[]="123456";
void fun1();
#endif
/*test1.cpp*/
#include "test1.h"
void fun1()
{
      cout <<g_str<<endl;
}

/*test2.cpp*/
#include "test1.h"
void fun2()
{
      cout <<g_str<<endl;
}

以上两个编译单元能够连接成功。当你打开test1.obj时,你能够在它里面找到字符串"123456"。同一时候你也能够在test2.obj中找到它们。它们之所以能够连接成功而没有报反复定义的错误是由于尽管它们有同样的内容,可是存储的物理地址并不一样,就像是两个不同变量赋了同样的值一样。而这两个变量分别作用于它们各自的编译单元。

或许你比較较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1,test2)的g_str的内存地址同样。于是你下结论static修饰的变量也能够作用于其它模块,可是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,运行效率更高,当编译器在连接各个编译单元的时候,它会把同样内容的内存仅仅拷贝一份,比方上面的"123456"。位于两个编译单元中的变量都是同样的内容,那么在连接的时候它在内存中就仅仅会存在一份了,假设你把上面的代码改成以下的样子,你立即就能够拆穿编译器的谎言:

/*test1.cpp*/
#include "test1.h"
void fun1()
{
      g_str[0]=''a'';
      cout <<g_str<<endl;
}
/*test2.cpp*/
#include "test1.h"
void fun2()
{
      cout <<g_str<<endl;
}
/*main.cpp*/
void main()
{
      fun1();// a23456
      fun2();// 123456
}

这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同。由于你在一处改动了它,所以编译器被强行的恢复内存的原貌。在内存中存在了两份拷贝给两个模块中的变量使用。正是由于static有以上的特性,所以一般定义static全局变量时。都把它放在原文件里而不是头文件。这样就不会给其它模块造成不必要的信息污染。相同记住这个原则吧!

四、extern和const

C++中const修饰的全局常量具有跟static同样的特性,即它们仅仅能作用于本编译模块中,且static修饰的是全局变量,可是const能够与extern连用来声明该常量能够作用于其它编译模块中,如externconst
char g_str[];

然后在原文件里别忘了定义:const char g_str[] = "123456";

所以当const单独使用时它就与static同样,而当与extern一起合作的时候,它的特性就跟extern的一样了!

所以对const我没有什么能够过多的描写叙述。我仅仅是想提醒你,const
char* g_str = "123456" 与 const char g_str[] ="123465"是不同的,前面那个const修饰的是char *而不是g_str,它的g_str并非常量。它被看做是一个定义了的全局变量(能够被其它编译单元使用)。 所以假设你像让char* g_str遵守const的全局常量的规则,最好这么定义const
char* const g_str="123456"。

时间: 2024-08-03 14:06:25

深入理解extern使用方法的相关文章

C#Lambda表达式的理解:谓词方法 匿名方法 使用Lambda

Lambda表达式 "Lambda表达式"是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量.它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式.所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to".Lambda运算符的左边是输入参数(如果有),右边是表达式或语句块. 下面三个方法会帮你会容易理解到Lambda表达式的好处,

TableViewCell reuse 重用 我认为的正确理解与使用方法

其实有点失望,因为用google搜索“uitableviewcell dequeueReusableCellWithIdentifier”出来一堆堆资深博主的文章.看了看,大部分都是在解决一个问题:使用重用时cell显示混乱的问题.该问题本身并不让我失望,失望的是博主们的解释. 首先,回顾一下UITableViewCell的重用,其基本逻辑就是tableView一开始会创建一屏幕的cell(如果有那么多)并把他们标记(Identifier),之后用户上下滑动tableView时,使用Identi

百度Android语音识别SDK语义理解与解析方法

百度语义理解开放平台面向互联网开发者提供自然语言文本的解析服务,也就是可以根据文本的意图解析成相应的表示. 为了易于人阅读,同时也方便机器解析和生成,意图表示协议采用 json 语言进行描述,采用 gb18030 编码. json 语言的基本概念: 1.属性名/属性值 即键值对(key-value) 2.数组 在 json 中是"[]"括起来的内容,数据结构为 ["value1","value2",...],取值方式和所有语言中一样,使用索引获取

计划:怎样理解水平集方法 ITK Level set V4 框架介绍

简易解释:在曲面中插入一个平面所形成的轮廓,即是该轮廓的水平集表示,可见,该轮廓的水平集表示有多个.对于图像分割,在图像力的驱动下曲面进行更新. 轮廓的数学表达有隐式和显式两种表达.用曲面演化代替Front (C)演进. C(t) = {(x, y)|φ(x, y, t) = 0} ?φ/ ?t + F|?φ| =0 (1) φ(x, y, 0) = φ0(x, y) 方程的本质是什么? 几何解释是什么 edge-based level set ?φ /?t = g(?I)|?φ| (div (

extern 使用方法具体解释

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明"此变量/函数是在别处定义的.要在此处引用".(extern能够置于变量或者函数前,以标示变量或者函数的定义在别的文件里,提示编译器遇到此变量和函数时在其它模块中寻找其定义 ) 大概extern  使用方法为例如以下几种方式: 其主要使用方法是: 在此文件里声明别的文件的变量时用extern 在cpp程序文件里用到c的库函数时用extern 头文件.h extern volatile SERV103_LINK servLi

理解 ES6 Generator-next()方法

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>generator-next</title> 6 </head> 7 <body> 8 9 <h3>理解 ES6 Generator-next() 方法</h3> 10 11 <scr

【JVM虚拟机】(8)--深入理解Class中--方法、属性表集合

#[JVM虚拟机](8)--深入理解Class中--方法.属性表集合 之前有关class文件已经写了两篇博客: 1.[JVM虚拟机](5)---深入理解JVM-Class中常量池 2.[JVM虚拟机](6)---深入理解Class中访问标志.类索引.父类索引.接口索引 3.[JVM虚拟机](7)---深入理解Class中-属性集合 那么这篇博客主要讲有关 方法表集合 相关的理解和代码示例. 方法表集合: 告知该方法是什么修饰符修饰?是否有方法值?返回类型是什么?方法名称,方法参数,还有就是方法内

连续张量理解和contiguous()方法使用,view和reshape的区别

连续张量理解和contiguous()方法使用,view和reshape的区别 待办 内存共享: 下边的x内存布局是从0开始的,y内存布局,不是从0开始的张量 For example: when you call transpose(), PyTorch doesn't generate new tensor with new layout, it just modifies meta information in Tensor object so offset and stride are f

extern外部方法使用C#简单例子

外部方法使用C#简单例子 1.增加引用using System.Runtime.InteropServices; 2.声明和实现的连接[DllImport("kernel32", SetLastError = true)] 3.声明外部方法public static extern int GetCurrentDirectory(int a, StringBuilder b); 4.对外部方法操作  GetCurrentDirectory(300, pathstring); using