C++中的extern声明变量详解

extern声明变量无外乎如下两种:

1、声明全局变量
2、声明函数

今天我们只谈extern,什么const、static之类等等与之相关或不相关的一律忽略,下面就分别对以上两种情况一一讲解

声明和定义
既然提到extern声明变量,那我们就必须搞清楚声明和定义的区别。

这里我们将普通数据变量和函数统称变量。从内存分配角度来说,声明和定义的区别在于声明一个变量不会分配内存,而定义一个变量会分配内存。一个变量可以被声明多次,但是只能被定义一次。

基于以上前提,我们可以把声明和定义类比为指针和内存的关系。我们知道,指针其实就是指向内存的一个符号,变量的定义就好比一块内存区域,而声明就好比它的指针,可以有多个指针指向同一个内存区域,而一个指针只能指向一个内存区域,这样就很好理解为什么变量只能被定义一次,如果被定义多次,那就会分配多个内存,这样你通过变量的声明到底去找哪块内存区域呢,这会是个问题。

对于数据来说,声明和定义往往是同时存在的,比如下面的一行语句

代码如下:

int data;

这样既声明了data同时也定义了data,怎样做到只声明而不定义呢,用extern就可以了

代码如下:

extern int data;

对于函数来说,声明和定义就很容易区分了,一般我们会将声明放在头文件而将定义放在源文件里

代码如下:

void hello();

这是一个函数的声明,而

代码如下:

void hello() 

    printf("hello world!\n"); 
}

这是一个函数的定义。当然,函数的声明和定义也可以同时发生,如果我们没有头文件而只有源文件,并且在源文件里并没有void hello();这样的语句,那么这个函数的声明和定义就同时发生了,此时如果我们在原文件里想要调用函数hello(),你调用的代码必须在函数定义之后。

其实上面的要点只在于一句话:使用变量之前必须声明,声明可以有多次,而定义只能有一次。记住这句话,后面的就都很容易理解了。

extern声明全局变量

我们先来看如下例子,现有三个文件:test.h, test.cpp, main.cpp,其中main.cpp和test.cpp需要共享一个变量g_name,三个文件的内容如下

代码如下:

/* test.h */ 
#ifndef _TEST_H_ 
#define _TEST_H_ 
 
#include <string> 
 
std::string g_name; 
void hello(); 
 
#endif 
 
/* test.cpp */ 
#include <stdio.h> 
#include "test.h" 
 
void hello() 

    printf("hello %s!\n", g_name.c_str()); 

 
/* main.cpp */ 
#include "test.h" 
 
std::string g_name; 
 
int main() 

    g_name = "Handy"; 
    hello(); 
    return 0; 
}

三者关系为,test.cpp包含了test.h,main.cpp也包含了test.h,这里的包含其实就是include。我们执行编译命令

代码如下:

g++ main.cpp test.cpp

编译报错redefinition of 'g_name',说的是g_name被重定义了

我们看一下g_name出现的地方,一个是在test.h里,一个是在main.cpp里,两条语句都是std::string g_name,前面我们已经说过,这样的方式既声明也定义了变量,那g_name是如何被重定义的呢,首先我们需要理解include的含义,我们可以将include一个头文件理解为在该行展开头文件里的所有代码,由于main.cpp包含了test.h,我们在那一行将test.h的内容展开,就会发现main.cpp里有两句std::string g_name;所以在main.cpp里,g_name被定义了两次。

由于我们可以将include头文件理解为展开代码,所以编译的时候其实不需要指定头文件,只需要源文件就够了。需要注意的是,重定义并不是指在同一个原文件里定义多次,而是指在整个代码空间里,比如上面的例子是就是指在test.cpp和main.cpp里,其实上面的例子里g_name是被重定义了三次,其中test.cpp里一次,main.cpp里两次。

那上面重定义的问题怎么解决呢,很简答,将test.h里的std::string g_name;改为extern std::string g_name;就可以了,由于extern语句只声明变量而不定义变量,因此test.cpp和main.cpp展开头文件后,也只是将g_name声明了两次,而真正的定义还是在main.cpp里

extern声明函数

还是上面的例子,我们怎么在main.cpp里不包含头文件就可以调用hello函数呢,既然今天的主题是extern,不用提醒也知道,使用extern就可以了,代码如下

代码如下:

/* test.cpp */ 
#include <string> 
#include <stdio.h> 
 
// 声明g_name 
extern std::string g_name;         
 
// 声明和定义void hello() 
void hello()                       

    printf("hello %s!\n", g_name.c_str()); 

 
/* main.cpp */ 
#include <string> 
 
// 声明和定义g_name 
std::string g_name;    
 
// 声明void hello()            
extern void hello();              
 
int main() 

    g_name = "Handy" 
    hello(); 
    return 0; 
}

注意这里用到extern声明变量和函数两种场景,我分别在语句后面做了注释。编译命令如下

代码如下:

g++ main.cpp test.cpp

这里我们并没有用到头文件,但是依然可以在不同文件间共享变量和函数,这一切都是extern的功劳!

总结

要了解extern主要搞清以下几个概念:

1、声明和定义的区别。全局代码空间里,变量可以有多个声明,但只能有一个定义
2、include头文件等同于展开头文件里的代码

了解了以上两点,再来分析extern的用法,是不是就会清晰很多了

(0)

相关推荐

  • C++中的extern “C”用法详解

    简单来说,extern "C"是C++声明或定义C语言符号的方法,是为了与C兼容.说来容易,要理解起来还是得费些周折,首先我们要从C++和C的区别说起. 符号 大家都知道,从代码到可执行程序需要经过编译和链接两个过程,其中编译阶段会做语法检测,代码展开,另外它还会做一件事,就是将变量转成符号,链接的时候其实是通过符号来定位的.编译器在编译C和C++代码时,将变量转成符号的过程是不同的.本文所使用的编译器为gcc4.4.7 我们先来看一段简单的代码 复制代码 代码如下: /* hello

  • C++中extern "C"的用法

    学习过C++的人都知道,extern关键字可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.这里起到的是声明作用范围的用处.另外,extern还可以与"C"连用,作为链接指示.本文就此进行实例说明如下: 一.C++名字修饰(Name Mangling) 首先需要从C++的重载说起,在C++中函数重载指的是几个函数的函数名相同,参数列表不同.那么当生成obj中间文件/目标文件的时候,C++编译器如何区分这几个重载函数呢?可以

  • C/C++ 中extern关键字详解

    C/C++ 中extern关键字详解 在C/C++编程过程中,经常会进行变量和函数的声明和定义,各个模块间共用同一个全局变量时,此时extern就派上用场了. 定义 extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义,不需要分配内存,直接使用. 推荐:在.h中声明,因为在头文件定义的话,其他模块include此头文件,就会报重复定义错误 实验结论 1.在.h中声明 extern int g_a; 在.c中定义 int g_

  • 从汇编看c++中extern关键字的使用

    在c++中,extern关键字用来声明变量和函数,在声明函数的时候,有和没有extern的效果一样,即下面两条语句具有同样的效果: 复制代码 代码如下: extern void fun(); void fun(); 但是对于变量,有和没有extern就有区别,当有extern时,只是告知编译器存在这个变量,编译器并不为该变量分配存储空间,即真正的声明:若没有extern,则在声明的同时,编译器也为该变量分配存储空间. 下面是有extern的情形时的c++源码: 复制代码 代码如下: int ma

  • 实例详解C/C++中extern关键字

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是f

  • 深入理解C语言 static、extern与指针函数

    1.exit(0)正常退出程序 exit(1)程序异常时退出程序 2.static(静态变量)修饰局部变量 在局部变量使用static修饰,会延长局部变量的存在期.但我们需要注意一下几点: •虽然static修饰变量的生存期很长,但它始终是局部变量,不能在其他函数中使用•static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?     全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量.全局变量本身

  • C/C++中extern "C" 的作用分析

    我们经常会在C/C++程序中见到extern "C",这是一个很重要的概念.本文就来以实例形式讲述C/C++中extern "C"的作用.分享给大家供大家参考之用.具体分析如下: 作用:实现C和C++混合编程. 原理:C和C++编译器编译之后,函数名会编译成不同的名字,链接阶段名字查找会找不到目标,后面实例中会详解. 用法: ①.c文件中定义的函数,.cpp文件要调用时,该.cpp文件中要用extern "C"声明该函数: ②.反过来,.cpp文

  • C/C++混合编程之extern “C”的使用示例

    前言 本文主要给大家介绍了关于C/C++混合编程extern "C"使用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先要明白: C++号称是C语言的超集,也确实,从语言的基本语法上,C++是包含所有C语言的语法的,而且C++为了兼容C,连C语言的标准库也被纳入到C++的标准库中,比如在C++中我们仍然可以使用<stdio.h> ,它就是C++标准库的一部分(注意最好用新的标准<cstdio> ,而不是老的<stdio&g

  • 浅谈C/C++中的static与extern关键字的使用详解

    一.C语言中的static关键字在C语言中,static可以用来修饰局部变量,全局变量以及函数.在不同的情况下static的作用不尽相同.(1)修饰局部变量一般情况下,对于局部变量是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时便结束了.但是如果用static进行修饰的话,该变量便存放在静态数据区,其生命周期一直持续到整个程序执行结束.但是在这里要注意的是,虽然用static对局部变量进行修饰过后,其生命周期以及存储空间发生了变化,但是其作用域并没有改变,其仍然是一个局部变量,作用域仅

  • C++中的extern声明变量详解

    extern声明变量无外乎如下两种: 1.声明全局变量 2.声明函数 今天我们只谈extern,什么const.static之类等等与之相关或不相关的一律忽略,下面就分别对以上两种情况一一讲解 声明和定义 既然提到extern声明变量,那我们就必须搞清楚声明和定义的区别. 这里我们将普通数据变量和函数统称变量.从内存分配角度来说,声明和定义的区别在于声明一个变量不会分配内存,而定义一个变量会分配内存.一个变量可以被声明多次,但是只能被定义一次. 基于以上前提,我们可以把声明和定义类比为指针和内存

  • 在JS中如何使用css变量详解

    在JS中如何使用css变量 使用:export关键字在less/scss文件中导出一个js对象. $menuText:#bfcbd9; $menuActiveText:#409EFF; $subMenuActiveText:#f4f4f5; // $menuBg:#304156; $menuBg:#304156; $menuHover:#263445; $subMenuBg:#1f2d3d; $subMenuHover:#001528; $backWhite:#ffffff; $sideBarW

  • C/C++中extern函数使用详解

    目录 一.定义和声明的区别 二.extern用法 2.1 extern 函数 2.2 extern 变量 2.3 在C++文件中调用C方式编译的函数 三.通俗讲解extern 一.定义和声明的区别 声明用来告诉编译器变量的名称和类型,而不分配内存,不赋初值. 定义为了给变量分配内存,可以为变量赋初值. 注:定义要为变量分配内存空间:而声明不需要为变量分配内存空间. 二.extern用法 2.1 extern 函数 为什么要用extern 函数呢?直接#include相应的头文件不可以嘛? 例子,

  • Java类和成员变量声明类详解

    目录 声明类 声明成员变量 访问修饰符 类型 变量名 声明类 定义类: class MyClass { // 字段.构造函数和 // 方法声明 } 这是一个类声明.类主体(大括号之间的区域)包含提供从类创建的对象的生命周期的所有代码: 用于初始化新对象的构造函数,提供类及其对象状态的字段的声明,以及方法来实现类及其对象的行为. 前面的类声明是最小的.它仅包含所需的类声明的那些组件.您可以在类声明的开头提供有关该类的更多信息,例如其超类的名称.是否实现任何接口等. 例如: class MyClas

  • C语言在头文件中定义const变量详解

    C语言在头文件中定义const变量详解 在头文件中定义const不会有多变量的警告或错误,如果该头文件被大量包含会造成rom空间的浪费. 通过查看*.i文件的展开呢,可以发现每个.i文件都会有相应的变量展开. 查看*.map文件,能查看到该变量的多个地址分配. 在预编译的时候如果在头文件定义了const变量,每一个包含该头文件的c文件都会将其展开,而在编译的时候不会报错,因为这符合语法规则,每一个包含这个头文件的*.c文件都会编译一次这个变量,分配一个新的地址,然后在链接的时候也不会报错,因为每

  • 在Vue mounted方法中使用data变量详解

    如下所示: data: { certificates: null }, mounted: function () { var __this = this; __this.certificates = getDictForkey("学历"); } 使用this对data中变量进行调用 vue生命周期参照官方:点击进入 vue渲染页面的时候,可能会对某些数据进行字典操作,比方性别,数据中是0和1,字典值是男和女,这个时候就需要在mounted进行"性别"字典的获取,然后

  • PyTorch中的Variable变量详解

    一.了解Variable 顾名思义,Variable就是 变量 的意思.实质上也就是可以变化的量,区别于int变量,它是一种可以变化的变量,这正好就符合了反向传播,参数更新的属性. 具体来说,在pytorch中的Variable就是一个存放会变化值的地理位置,里面的值会不停发生片花,就像一个装鸡蛋的篮子,鸡蛋数会不断发生变化.那谁是里面的鸡蛋呢,自然就是pytorch中的tensor了.(也就是说,pytorch都是有tensor计算的,而tensor里面的参数都是Variable的形式).如果

  • Python中可变变量与不可变变量详解

    目录 一 .常见的变量分类 1.变量的创建 二.变量分类 1..常见的不可变变量 2.常见的可变变量 三.拷贝的差别 四.参数传递的差别 前言: C++不同于Python的显著特点,就是有指针和引用,这让我们在调用参数的时候更加清晰明朗.但Python中没有指针和引用的概念,导致很多时候参数的传递和调用的时候会产生疑问:我到底是复制了一份新的做操作还是在它指向的内存操作? 这个问题根本上和可变.不可变变量有关,我想把这个二者的区别和联系做一个总结,以更深入地理解Python内部的操作.我本身非科

  • Oracle中游标Cursor基本用法详解

    查询 SELECT语句用于从数据库中查询数据,当在PL/SQL中使用SELECT语句时,要与INTO子句一起使用,查询的 返回值被赋予INTO子句中的变量,变量的声明是在DELCARE中.SELECT INTO语法如下: SELECT [DISTICT|ALL]{*|column[,column,...]} INTO (variable[,variable,...] |record) FROM {table|(sub-query)}[alias] WHERE............ PL/SQL

随机推荐