转自

https://www.cnblogs.com/chengmin/archive/2011/09/26/2192008.html

当你要引用一个全局变量的时候,你就要声明,extern int a;这时候extern不能省略,因为省略了,就变成int a;这是一个定义,不是声明。

用#include可以包含其他头文件中变量、函数的声明,为什么还要extern关键字,如果我想引用一个全局变量或函数a,我只要直接在源文件中包含#include<xxx.h> (xxx.h包含了a的声明)不就可以了么,为什么还要用extern呢??这个问题一直也是似是而非的困扰着我许多年了,今天上网狠狠查了一下总算小有所获了:

头文件

首先说下头文件,其实头文件对计算机而言没什么作用,她只是在预编译时在#include的地方展开一下,没别的意义了,其实头文件主要是给别人看的。

我做过一个实验,将头文件的后缀改成xxx.txt,然后在引用该头文件的地方用

#include"xxx.txt"

编译,链接都很顺利的过去了,由此可知,头文件仅仅为阅读代码作用,没其他的作用了!

不管是C还是C++,你把你的函数,变量或者结构体,类啥的放在你的.c或者.cpp文件里。然后编译成lib,dll,obj,.o等等,然后别人用的时候最基本的gcc hisfile.cpp yourfile.o|obj|dll|lib 等等。
但对于我们程序员而言,他们怎么知道你的lib,dll...里面到底有什么东西?要看你的头文件。你的头文件就是对用户的说明。函数,参数,各种各样的接口的说明。
那既然是说明,那么头文件里面放的自然就是关于函数,变量,类的“声明”了。记着,是“声明”,不是“定义”。
那么,我假设大家知道声明和定义的区别。所以,最好不要傻嘻嘻的在头文件里定义什么东西。比如全局变量:

#ifndef _XX_头文件.H
#define _XX_头文件.H
int A;
#endif

那么,很糟糕的是,这里的int A是个全局变量的定义,所以如果这个头文件被多次引用的话,你的A会被重复定义
显然语法上错了。只不过有了这个#ifndef的条件编译,所以能保证你的头文件只被引用一次,不过也许还是会岔子,但若多个c文件包含这个头文件时还是会出错的,因为宏名有效范围仅限于本c源文件,所以在这多个c文件编译时是不会出错的,但在链接时就会报错,说你多处定义了同一个变量,

Linking...
incl2.obj : error LNK2005: "int glb" (?glb@@3HA) already defined in incl1.obj
Debug/incl.exe : fatal error LNK1169: one or more multiply defined symbols found

注意!!!

extern

这个关键字真的比较可恶,在声明的时候,这个extern居然可以被省略,所以会让你搞不清楚到底是声明还是定义,下面分变量和函数两类来说:

(1)变量

尤其是对于变量来说。
extern int a;//声明一个全局变量a
int a; //定义一个全局变量a
extern int a =0 ;//定义一个全局变量a 并给初值。
int a =0;//定义一个全局变量a,并给初值,
第四个 等于 第 三个,都是定义一个可以被外部使用的全局变量,并给初值。
糊涂了吧,他们看上去可真像。但是定义只能出现在一处。也就是说,不管是int a;还是extern int a=0;还是int a=0;都只能出现一次,而那个extern int a可以出现很多次。

当你要引用一个全局变量的时候,你就要声明,extern int a;这时候extern不能省略,因为省略了,就变成int a;这是一个定义,不是声明。

(2)函数
函数,函数,对于函数也一样,也是定义和声明,定义的时候用extern,说明这个函数是可以被外部引用的,声明的时候用extern说明这是一个声明。 但由于函数的定义和声明是有区别的,定义函数要有函数体,声明函数没有函数体,所以函数定义和声明时都可以将extern省略掉,反正其他文件也是知道这个函数是在其他地方定义的,所以不加extern也行。两者如此不同,所以省略了extern也不会有问题。
比如:
int fun(void)
{
return 0;
}
很好,我们定义了一个全局函数
int fun(void);
我们对它做了个声明,然后后面就可以用了
加不加extern都一样
我们也可以把对fun的声明 放在一个头文件里,最后变成这样
int fun(void);//函数声明,所以省略了extern,完整些是extern int fun(void);
int fun(void)
{
return 0;
}//一个完整的全局函数定义,因为有函数体,extern同样被省略了。
然后,一个客户,一个要使用你的fun的客户,把这个头文件包含进去,ok,一个全局的声明。没有问题。
但是,对应的,如果是这个客户要使用全局变量,那么要extern 某某变量;不然就成了定义了。

总结下:

对变量而言,如果你想在本源文件中使用另一个源文件的变量,就需要在使用前用extern声明该变量,或者在头文件中用extern声明该变量;

对函数而言,如果你想在本源文件中使用另一个源文件的函数,就需要在使用前用声明该变量,声明函数加不加extern都没关系,所以在头文件中函数可以不用加extern。

C程序采用模块化的编程思想,需合理地将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求,在模块的划分上主要依据功能。模块由头文件和实现文件组成,对头文件和实现文件的正确使用方法是:
规则1 头文件(.h)中是对于该模块接口的声明,接口包括该模块提供给其它模块调用的外部函数及外部全局变量,对这些变量和函数都需在.h中文件中冠以extern关键字声明;
规则2 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;
规则3 永远不要在.h文件中定义变量;
许多程序员对定义变量和声明变量混淆不清,定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如:
int a = 5;
#include “module1.h”
#include “module1.h”
#include “module1.h”
以上程序的结果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这明显不符合编写者的本意。正确的做法是:
extern int a;
#include “module1.h”
int a = 5;
#include “module1.h”
#include “module1.h”  
这样如果模块1、2、3操作a的话,对应的是同一片内存单元。

规则4 如果要用其它模块定义的变量和函数,直接包含其头文件即可。
许多程序员喜欢这样做,当他们要访问其它模块定义的变量时,他们在本模块文件开头添加这样的语句:
extern int externVar; 
抛弃这种做法吧,只要头文件按规则1完成,某模块要访问其它模块中定义的全局变量时,只要包含该模块的头文件即可。

共享变量声明
就像在函数间共享变量的方式一样,变量可以在文件中共享。为了共享函数,要把函数的定义放在一个源文件中,然后在需要调用此函数的其他文件中放置声明。共享变量的方法和此方式非常类似。
在此之前,不需要区别变量的声明和它的定义。为了声明变量i,写成如下形式:
int i; 
这样不仅声明i是int型的变量,而且也对i进行了定义,从而使编译器为i留出了空间。为了声明没有定义的变量i,需要在变量声明的开始处放置关键字extern:
extern int i;
extern提示编译器变量i是在程序中的其他位置定义的(大多数可能是在不同的源文件中),因此不需要为i分配空间。
顺便说一句,extern可以用于所有类型的变量。在数组的声明中使用extern时,可以忽略数组的长度:
extern int a[];
因为此刻编译器不用为数组a分配空间,所以也就不需要知道数组a的长度了。
为了在几个源文件中共享变量i,首先把变量i的定义放置在一个文件中:
int i;
如果需要对变量i初始化,那么可以在这里放初始值。在编译这个文件时,编译器将会为变量i分配内存空间,而其他文件将包含变量i的声明:
extern int i;
通过在每个文件中声明变量i,使得在这些文件中可以访问/或修改变量i。然而,由于关键字extern,使得编译器不会在每次编译其中某个文件时为变量i分配额外的内存空间。
当在文件中共享变量时,会面临和共享函数时相似的挑战:确保变量的所有声明和变量的定义一致。
为了避免矛盾,通常把共享变量的声明放置在头文件中。需要访问特殊变量的源文件可以稍后包含适当的头文件。此外,含有变量定义的源文件包含每一个含有变量声明的头文件,这样使编译器可以检查两者是否匹配。

如果工程很大,头文件很多,而有几个头文件又是经常要用的,那么
1。把这些头文件全部写到一个头文件里面去,比如写到preh.h
2。写一个preh.c,里面只一句话:#include "preh.h"
3。对于preh.c,在project setting里面设置creat precompiled headers,对于其他
c文件,设置use precompiled header file

转载于:https://www.cnblogs.com/focus-z/p/10274687.html

extern 用法,全局变量与头文件(重复定义)相关推荐

  1. c语言头文件可以定义全局变量,C语言在头文件中定义全局变量

    C语言在头文件中定义全局变量 头文件定义全局变量等问题 全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么? 可以,在不同的C文件中以static形式来声明同名全局变量.头文件中不可以直接定 ...

  2. 能不能在头文件中定义全局变量?

    首先,这是一篇科普文,所以 比较杂,我尽量写清楚一些. 1.ANSI C标准是什么?GNU又是什么?ld是什么? ANSI C是C语言的标准规范,是国际标准化组织制定的国际标准. 虽然 ANSI C规 ...

  3. C++声明、定义、类的定义、头文件作用、头文件重复引用

    转载至:点击打开链接 C++声明.定义.类的定义.头文件作用.头文件重复引用,不具名空间 转自:http://www.cnblogs.com/rocketfan/archive/2009/10/02/ ...

  4. 头文件中定义全局变量

    引言 这些天写的程序中用到了全局变量,一开始是在一个文件下做测试后来把文件按逻辑拆分了一下,不同的代码被放在了几个文件中,最后用Makefile来编译就遇到了今天的话题,怎么在头文件中定义全局变量呢? ...

  5. 套头文件html重复,关于C++头文件重复包含的问题

    #ifndef PEOPLE_H #define PEOPLE_H /* - */ #endif 这样写能够防止头文件被重复包含 在头文件中定义变量不是不规范,而是一种错误. 原因在于 如果在head ...

  6. 头文件源文件定义标准

    头文件源文件定义标准 @TOC- 前言 头文件源文件定义标准(自己理解) Function1.h #pragma once #ifndef ADI_HEADER #define ADI_HEADERe ...

  7. 【自我修养】不要嘻嘻哈哈的在头文件中定义变量

    在头文件中直接定义变量甚至定义加上赋值,是非常没有修养的行为,新手是经常这样干,有的老手也不注意,这是不应该的. 在头文件中定义变量会出现这些问题: 1,出现变量重复定义的错误.如果你在头文件中定义了 ...

  8. C++中为什么不能将全局变量定义在头文件中?

    一.什么是全局变量? C++中全局变量一般指定义在函数体外的变量. 全局变量按可访问性可分为外部变量和内部变量. 二.内部变量和外部变量的定义 内部变量:使用了static关键字修饰的全局变量.它的可 ...

  9. C++头文件重复包含问题分析及解决方案

    一.头文件重复包含问题分析 1) 问题重现 举例说明.假设在某个C++ 头文件 或 源文件 中,包含了A.h和B.h两个头文件: #include "A.h" #include & ...

最新文章

  1. 对‘初学者应该选择哪种编程语言’的回答——计算机达人成长之路(38)
  2. eclipse中格式化代码快捷键Ctrl+Shift+F失效的解决办法
  3. linux 占用cpu 脚本,消耗CPU资源的shell脚本
  4. 【Python】可视化分类型变量,我一般使用这6种图形。
  5. js检测开发者工具Devtools是否打开防调试
  6. SAP ABAP DDICSAP ABAP DDIC table runtime object table runtime object
  7. linux显示点阵字体,Fedora 17中文字体显示点阵状的解决方法
  8. 车辆特征系数——车速的计算
  9. android Handler UI线程后台线程通信
  10. ubuntu mysql开发环境_Ubuntu + Nginx/PHP/MYSQL开发环境配置图文教程
  11. 【报告分享】2022年中国商业十大热点展望.pdf(附下载链接)
  12. 怎么查江苏省计算机一级成绩,江苏省计算机一级查询成绩在哪里查-江苏省计算机一级查询成绩查询网址-常州宝...
  13. python--split方法
  14. Struts1.x系列教程(19):LookupDispatchAction类处理一个form多个submit
  15. k8s的优势和部署模式
  16. 解决-redis保存的中文变为unicode, redis的基本操作
  17. Java项目:小区物业管理系统(java+Springboot+ssm+mysql+maven+jsp)
  18. html5 canvas画彩虹,HTML5 Canvas彩虹连接点动画
  19. Java.day17
  20. Kotlin-Android世界的一股清流-Lambda表达式

热门文章

  1. BASIC-12 十六进制转八进制
  2. 使用C++实现DPCM编码(左向预测8bit、4bit、2bit、1bit和上向预测8bit)(更新过)
  3. 【Qt】Qt中QJsonArray类
  4. 【Linux】一步一步学Linux——adduser命令(83)
  5. 【Linux】一步一步学Linux——dirs命令(了解)(24)
  6. 【Linux】一步一步学Linux——虚拟机简介和系统要求(04)
  7. 【Tiny4412】 编译dnw源码报错 /lib/modules/2.6.32-431.el6.x86_64/build/: No such file or directory
  8. 判断一个点是否在矩形内部_高速公路专用矩形泄水管特点及安装注意事项
  9. 计算机屏幕显示电缆借口,电脑关机后显示器显示请检查电缆接口怎么办成功解决...
  10. php mysql数据库 指南_用 PHP 创建 MySQL 数据库