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

 更新时间:2015年03月17日 09:39:57   投稿:junjie  
这篇文章主要介绍了C++中的extern声明变量详解,本文讲解了声明和定义、extern声明全局变量、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的用法,是不是就会清晰很多了

相关文章

  • 详解VSCode下C++环境配置过程

    详解VSCode下C++环境配置过程

    这篇文章主要介绍了VSCode C++环境配置过程,在这大家需要在代码的目录下的.vscode文件夹下创建launch.json、tasks.json,具体实现过程跟随小编一起看看吧
    2021-11-11
  • c++中的消息框messagebox()详细介绍及使用方法

    c++中的消息框messagebox()详细介绍及使用方法

    本文将介绍下c++中的messagebox()的使用方法:常用属性/按钮的形式/返回值等等,感兴趣的朋友可以了解下,希望本文可以帮助到你
    2013-02-02
  • C++序列操作函数学习最全指南

    C++序列操作函数学习最全指南

    这篇文章主要给大家介绍了关于C++序列操作函数学习的相关资料,文中通过实例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友可以参考下
    2022-02-02
  • c语言求两个字符串的交集

    c语言求两个字符串的交集

    大家好,本篇文章主要讲的是c语言求两个字符串的交集,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • C++中单链表操作的示例代码

    C++中单链表操作的示例代码

    这篇文章主要为大家详细介绍了C++中单链表操作的示例代码,主要包括单链表的创建、元素的插入和删除等。文中的代码简洁易懂,需要的可以参考一下
    2022-10-10
  • C++中四种强制转换方式的区别

    C++中四种强制转换方式的区别

    在C++中,有四种不同的强制转换方式,它们分别是静态转换、动态转换、常量转换和重新解释转换,下面通过示例代码讲解每种转换的区别,感兴趣的朋友跟随小编一起看看吧
    2023-08-08
  • C++结构体与类的区别详情

    C++结构体与类的区别详情

    这篇文章主要介绍了C++结构体与类的区别,C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。下面我们一起进入文章俩姐具体内容,需要的朋友也可以参考一下
    2021-11-11
  • 在Centos7中使用vscode和gdb调试PG插件的方法

    在Centos7中使用vscode和gdb调试PG插件的方法

    这篇文章主要介绍了在Centos7中使用vscode和gdb调试PG插件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • C++类和对象之类的6个默认成员函数详解

    C++类和对象之类的6个默认成员函数详解

    类是对某一事物的抽象描述,具体地讲类是C++中的一种构造的数据类型,下面这篇文章主要给大家介绍了关于C++类和对象之类的6个默认成员函数的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-02-02
  • c字符串,string对象,字符串字面值的区别详解

    c字符串,string对象,字符串字面值的区别详解

    以下是对c字符串,string对象,字符串字面值的区别进行了详细的介绍,需要朋友可以 过来参考下
    2013-09-09

最新评论