extern详解

2023-11-18

extern——关键字

extern是C语言中的一个关键字,一般用在变量名前或函数名前,作用是用来说明“此变量/函数是在别处定义的,要在此处引用”,extern这个关键字大部分读者应该是在变量的存储类型这一类的内容中

遇到的,下面先分析C语言不同的存储类型

在C语言中变量和函数有数据类型存储类型两个属性,因此变量定义的一般形式为:存储类型 数据类型 变量名表;

C语言提供了一下几种不同的存储类型:

(1)  自动变量(auto)

(2)  静态变量(static)

(3)  外部变量(extern)

(4)  寄存器变量(register)

(上面的auto、static、extern、register都是C语言的关键字),这里只分析extern关键字的使用

外部变量(全局变量)extern----全局静态存储区

标准定义格式:extern 类型名 变量名;

如果在所有函数之外定义的变量没有指定其存储类别,那么它就是一个外部变量,它的作用域是从它的定义点到本文件的末尾(在单个源文件中的确是这样,如果有多个源文件,全局变量的作用范围不是从变量定义处到该文件结尾,而是在其他文件中也有效),但是如果要在定义点之前或者其他文件中使用它,那么就需要使用关键字extern对其进行声明(注意不是定义,编译器并不为其分配内存)

Tips:

定义:表示创建变量或分配存储单元

声明:说明变量的性质,但并不分配存储单元

extern int i; //是声明,不是定义,没有分配内存
int i; //是定义

如果在声明的时候给变量赋值,那么就和去掉extern直接定义变量赋值是等价的

extern int a = 10;//尽量不要写这种定义方式
int a = 10;//上述两条语句等价

(注意上面的不同语句对声明和定义的区分,对源文件中的局部变量来说是成立的(也就是.c文件),而对于源文件中的全局变量(外部变量)int a和在头文件中的int a就不能用上面的语句来解释声明和定义的区别)

补充:定义和声明的一个小坑,对于int a;来说,在源文件中,如果是全局变量的话就是声明,如果是局部变量的话就是定义

全局变量:

文章前面提到过一句话:如果在所有函数之外定义的变量没有指定其存储类别,那么它就是一个外部变量,意思就是这里的int a;等价于extern int a;相当于声明,声明是可以多次的

局部变量:

 

文章中只提到了extern(外部变量),这里补充下auto(自动变量),由于auto(自动变量)极为常用,所以C语言把它设计成缺省的存储类型,即auto 可以省略不写,在main函数内部的变量int a也是局部变量,

相当于auto int a;相当于定义,定义只能一次

 

谨记:声明可以多次,定义只能一次

外部变量保存在静态存储区内,在程序运行期间分配固定的存储单元,其生存期是整个程序的运行期,没有显式初始化的外部变量由编译程序自动初始化为0(extern说明符来扩展全局变量的作用域,

可以将全局变量的作用域扩展到其他文件,但不能限制全局变量的作用域

extern作用于函数名和变量名时的区别:

读者应该会发现,函数声明时并没有使用 extern 关键字,这是因为,函数的定义有函数体,函数的声明没有函数体,编译器很容易区分定义和声明,所以对于函数声明来说,有没有extern 都是一样的

但是作用于变量名时extern关键字就不是可有可无的了,全局变量在外部使用声明时,extern关键词是必须的,如果变量无extern修饰且没有显式的初始化,就成为了变量的定义,因此此时必须加extern,

全局变量在不指定初值时会自动初始化为0

多文件编程

C语言代码是由上到下依次执行的,不管是变量还是函数,原则上都要先定义再使用,否则就会报错。但在实际开发中,经常会在函数或变量定义之前就使用它们,这个时候就需要提前声明(extern)

头文件中包含的都是函数声明,而不是函数定义

最好不要在头文件中定义变量,例如全局变量

这里的int a是个全局变量的定义,所以如果这个头文件被多次引用的话,a会被重复定义,这显然是不允许的,下面举一个简单多文件编程的例子

从上面的四张图可以看出,一共用了3个文件,两个c文件(源文件)一个h文件(头文件),我在my.c中写了一个函数名为Max的函数,在头文件中声明了这个Max函数,在有main的这个test1.c这个

源文件中用#include "my.h"包含了这个头文件,然后在函数中就可以直接使用Max函数了(注意头文件中只是声明,没有定义,而且不建议在头文件中定义函数,会引起很多不必要的麻烦

在《高质量C/C++编程指南》一书中,对此也有说明:

【建议1-2-1】头文件中只存放“声明”,而不存放“定义”。

在C++语法中,类的成员函数可以在声明的同时被定义,并且自动成为内联函数。这虽然会带来书写上的方便,但却造成了风格不一致,弊大于利。建议将成员函数的定义分开,不论该函数体有多么小。

【建议1-2-2】不提倡使用全局变量,尽量不要在头文件中出现现象 extern int value 这类声明。

作者:奄奄不息 
原文:https://blog.csdn.net/qq_41209741/article/details/84108962 

问题一:如果在my.c这个文件中定义了一个全局变量,想要在main函数中使用,该怎么做呢?

方法一:在含有main函数的源文件test1.c中加extern声明

运行结果:

方法二:在头文件中my.h中对my.c中的全局变量进行声明,再在test1.c中include头文件my.h

运行结果:

防止C语言头文件被重复包含

头文件包含命令 #include 的效果与直接复制粘贴头文件内容的效果是一样的,预处理器实际上也是这样做的,它会读取头文件的内容,然后输出到 #include 命令所在的位置,头文件包含是一个

递归(循环)的过程,如果被包含的头文件中还包含了其他的头文件,预处理器会继续将它们也包含进来;这个过程会一直持续下去,直到不再包含任何头文件,这与递归的过程颇为相似,

递归包含会导致一个问题,就是重复引入同一个头文件,重复引入同一头文件有什么问题呢,当你在头文件中定义变量或者函数时(注意是定义不是声明,多次声明是没有问题的)多次引入头文件就会报“变量被多次定义”的错误

下面来还原这种错误:

上面一共使用了3个头文件k1.h、k2.h、my.h在k1.h和k2.h中都包含了my.h,而且在my.h中有一个全局变量int k = 10,在test1.c中包含了上述三个头文件

编译结果

解决方法:

方法一:使用条件编译#ifndef(如果读者对条件编译不了解,可以先看一下这一篇随笔预处理命令使用详解----#if、#endif、#undef、#ifdef、#else、#elif

解释一下上面的条件编译语句,如果_MY_H这个宏名没有被定义,那么定义_MY_H这个宏名,并且定义全局变量int k = 10,这样做就避免了头文件的重复包含引起的,变量或函数的重复定义问题

我们可以在stdio.h这个“标准输入\输出头文件”中看到类似的用法

格式就像下面这个样子:

#ifndef _INC_STDIO
#define _INC_STDIO
/* 头文件内容 */
#endif

这种宏保护方案使得程序员可以“任性”地引入当前模块需要的所有头文件,不用操心这些头文件中是否包含了其他的头文件

但也不是没有缺点

#ifndef的方式依赖于宏名不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含

缺点就是如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况

方法二:使用#pragma once

#pramgma once是微软编译器独有的,也是后来才有的,所以知道的人并不是很多,用的人也不是很多,因为他不支持跨平台。如果你想写跨平台的代码,最好使用条件编译,如果想使用#pragma once,

只需在头文件开头加上#pragma once即可

 

#pragma带来的好处是:你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题,坏处也有,#pragma once是由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件,如果某个头文件有多份拷贝,这个方法就不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正

总结:1.#ifndef 由语言支持所以移植性好

    2.#pragma 可以避免名字冲突

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

extern详解 的相关文章

随机推荐

  • STM32单片机开发-01 STM32介绍

    通过野火开发板学习单片机 1 STM32介绍 1 1 STM32分类 从内核上分有Cortex M0 M3 M4 和M7 F1 代表了基础型 基于Cortex M3 内核 主频为72MHZ F4 代表了高性能 基于Cortex M4 内核
  • C#自定义一个简单的特性Attribute

    C 自定义一个简单的特性Attribute 首先我们自定义一个类 这个类继承Attribute 命名为MyAttribute 注意 要以Attribute结尾 然后在类中随便设置了一个属性 名叫Message 还创建了一个构造函数 这样一个
  • IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?

    背景知识 在设置电脑IP时 会碰到两个重要的参数 一个是IP地址 一个是子网掩码 IP地址是互联网上每个子网或每个主机在网络上的唯一身份标签 那子网掩码是干什么用的呢 随着互联网的发展 越来越多的网络产生 有的网络多则几百台主机 少则区区几
  • 神经网络学习小记录59——Pytorch搭建常见分类网络平台(VGG16、MobileNetV2、ResNet50)

    神经网络学习小记录59 Pytorch搭建常见分类网络平台 VGG16 MobileNetV2 ResNet50 学习前言 源码下载 分类网络的常见形式 分类网络介绍 1 VGG16网络介绍 2 MobilenetV2网络介绍 3 ResN
  • layui下拉框select通过Ajax动态加载数据后,动态渲染选中的值

    其中分为两种 1 select中的数据是在页面中写死的 需要从后端取值后判断选中哪一个 选择select的id 直接赋值 source val data obj source layui form render select 2 动态渲染s
  • nonce, timestamp, signatrue在Http安全协议中的作用

    OAuth协议 OAuth请求头里的nonce 随机数 timestamp 时间戳 signatrue 签名 Basic认证及其安全问题 Basic认证是一个流程比较简单的协议 整个过程可以分为以下三个步骤 客户端使用GET方法向服务器请求
  • [Binospace] Google-MegaStore的解读

    MegaStore是Google在BigTable之上实现了一个跨机房高可用的数据库 它提供了类似DB的数据分布 索引的功能 实现了在EntityGroup内部以及EntityGroup之间的事务性 并且通过Paxos协议实现在DC之间多备
  • 解决Uncaught SyntaxError: Unexpected reserved word

    解决思路 首先 我运行项目报错 我查看了一下node版本 是否太低 如果是14版本的话 那么node需要升级 目前 node已经升级到19 升级到16即可 无需太高 更新完node版本之后 发现它还是报错 然后接着从网上搜报错 经历无数次的
  • 【I2C】Linux使用GPIO模拟I2C

    文章目录 1 I2C GPIO系统架构简介 2 如何使能I2C GPIO驱动 2 1 config配置 2 2 dts配置 2 3 测试验证 3 简单分析i2c gpio c驱动 3 1 解析设备树 3 2 配置SDA和SCL 3 3 注册
  • vue进度条

  • 金九银十之面试闲谈

    文章目录 前言 面试流程 资料总结 刷题指南 个人经验总结 寄语 前言 今年的金九银十带着几分不确定性来了 加上各个大厂hc的收紧 今年的金九银十很难恢复往日的 荣光 不过肯定还是有很多毕业生或者其他原因的朋友们出来找工作 面试流程 面试流
  • Sharding-JDBC分布式事务总结(四)之BASE事务(Seat框架中——AT模式的介绍以及理解)

    Sharding分布式事务之BASE事务 Seat框架中 AT模式 1 什么是BASE事务 2 Seata框架的AT模式 2 1介绍 2 2原理 2 3特性 写隔离与读隔离 AT模式的 写隔离 读隔离 2 4优势 相较于XA事务 2 5启动
  • Windows设置本地DNS域名解析Hosts文件的方法

    我们需要先了解DNS解析查询的顺序 在用户输入域名之后 DNS解析查询的顺序是下面这样的 1 浏览器会首先查看自身的缓存 如果浏览器缓存中有对应的解析记录 直接返回结果 2 如果浏览器没有缓存 电脑会查看本地操作系统的缓存 如果有记录 直接
  • 2021我们相约一起用.NET改变Windows软件世界

    目录 成为C 版主 互联网启示录 改变 NET桌面应用 从替换Application Run开始 现在 让我们开始吧 令人惊讶的FirstApp exe 新起点从第一个Web页面开始 成为C 版主 不管最终是出于什么原因 我成为了C 论坛版
  • Basic Level 1034 有理数四则运算 (20分)

    题目 本题要求编写程序 计算 2 个有理数的和 差 积 商 输入格式 输入在一行中按照 a1 b1 a2 b2 的格式给出两个分数形式的有理数 其中分子和分母全是整型范围内的整数 负号只可能出现在分子前 分母不为 0 输出格式 分别在 4
  • MEM工程管理硕士的含金量与就业前景?

    MEM工程管理硕士的含金量与就业前景 修改 13年7月毕业 工作半年 想知道MEM现在的含金量怎么样 比起普通硕士而言呢 毕业前景如何 社会认可度高不高 我现在就比较想考这个 想在多学习学习 还有没有其他较好的选择 修改 举报 1 条评论
  • [4G&5G专题-123]:5G培训部署篇-1-5G网络架构与关键技术

    作者主页 https blog csdn net HiWangWenBing 文章出处 https blog csdn net HiWangWenBing article details 118437789 目录 第1部分 5G概述 第2部
  • HDMI之EDID使用说明

    Q1 为什么要写这篇文章 A1 在最近的工作中遇到了不少问题 其中很多都是和EDID相关的 可以说 作为一家以 显示 为生的企业 我们时时刻刻在与EDID打交道 EDID这东西很简单 但是如果不了解其基本原理和概念的话 会给我们的工作带来不
  • 服务器内存占用率76%,IT运维常见问题之一:服务器内存占有率高

    登录服务器一看 服务器也很卡 打开任务管理器 一看内存占有率99 了 在仔细一查看是 数据库占用了大量内存 打开数据库一看是部署的时候没有对数据库实例设置 最大服务器内存 下面就分享一下SQL Server数据库占用过高内存的处理方法 一
  • extern详解

    extern 关键字 extern是C语言中的一个关键字 一般用在变量名前或函数名前 作用是用来说明 此变量 函数是在别处定义的 要在此处引用 extern这个关键字大部分读者应该是在变量的存储类型这一类的内容中 遇到的 下面先分析C语言不