static,const,volatile,extern,register关键字深入解析

这篇具有很好参考价值的文章主要介绍了static,const,volatile,extern,register关键字深入解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!!
📃个人主页:@rivencode的个人主页
🔥系列专栏:玩转C语言
💬保持学习、保持热爱、认真分享、一起进步!!

一.最快的关键词-register

1.可执行的文件执行的本质是什么

我们都知道一个源文件要生成我们计算机课执行的文件要经过:
源文件(test.c)—>预编译—>编译—>链接—>可执行文件(test.exe)

对这个过程的详细描述请看 《预处理指令》

不管是源文件还是生成的可执行文件它们都是文件,而且都放在硬盘中!!

可执行的文件其实存放的是计算机可执行的指令(二进制代码),当我们去执行这个文件的时候屏幕会打印出执行后的结果,但大家有没有想过我们执行的这个文件的过程经历了什么。

运行可执行文件的本质是将数据(二进制指令)加载到内存,然后让CPU再去内存中读取数据(暂存在cpu中的寄存器或高速缓存中),经过CPU的计算好的数据又重新加回内存,然后从内存把结果打印到屏幕上
static,const,volatile,extern,register关键字深入解析

但这里有个问题,为啥CPU要经过一个内存读取数据,直接向硬盘读取数据不更好嘛,根本原因就是内存读写数据的速度比硬盘快的多.

这里就能理解定义变量为什么是在内存开辟空间:当程序执行起来的时候数据(二进制代码)已经加载到内存,包括定义变量的二进制指令,此时存储变量的空间只能是在内存中开辟。

2.最快的关键词register

CPU主要是负责进行计算的硬件单元,但是为了方便运算,一般第一步需要先把数据从硬盘写入内存,再提前将数据从内存读取到CPU内,那么也就需要CPU具有一定的数据临时存储能力,所以集成了一组叫做寄存器的硬件,用来做临时数据的保存,然后CPU一条一条指令执行运算,执行完后结果会写回内存。
static,const,volatile,extern,register关键字深入解析
既然寄存器的速度很快那我们能不能将变量直接存储在寄存器中呢,答案是可以的!!

register修饰变量

尽量将所修饰变量,会将变量一步到位放入CPU寄存器中,从而达到提高CPU效率的目的。

这里为什么是尽量呢,虽然我们用register修饰了变量,但编译器会判断这个变量值不值得放入寄存器,和能不能放入寄存器,所以编译器不一定会将该变量放入寄存器。

这样就引申出什么样的变量适合放入寄存器

  • 局部变量,全局变量最好不要,(全局变量生命周期是整个程序运行期间,所以会导致CPU寄存器被长时间占用)

  • 不会被写入(改变变量的值)的变量(因为一旦写入,运行结果就需要写回内存,后续还要读取检测的话,register修饰就没有意义了)

  • 变量会被高频被读取

  • register不能大量使用,因为寄存器数量有限

被register修饰的变量不能被取地址,原因就是地址是内存的中的概

static,const,volatile,extern,register关键字深入解析

二.声明关键字-extern

1.变量的生命周期与作用域

1.变量的定义与声明

变量的定义:需要在内存中开辟空间要存储数据,同时也可以初始化变量的值,但一个变量的定义只能有一次

声明:不管是函数的声明还是变量的声明,声明只是一个告知并不会开辟内存,告知编译器我们已经定义了可以使用,而且一个函数或变量的声明可以多次。

2.变量的生命周期与作用域

生命周期:指的是该变量从定义(开辟内存保存数据)到被释放存储空间的时间段记住它是一个时间概念。

作用域:指的是该变量能够被使用的范围,只有在作用域范围之内变量名才可以被使用。

3.全局变量与局部变量

  • 全局变量

定义:在函数外定义的变量叫做全局变量,存储在全局数据区,具有全局性

生命周期:定义完成后程序运行的整个生命周期内一直都有效。
作用域:整个工程

  • 局部变量

定义:包含在代码块( { } )中的变量叫做局部变量,存储栈区,局部变量具有临时性。

生命周期:从进入代码块形成局部变量开辟空间,退出代码块释放空间而经历这个过程的时间叫做局部变量的生命周期
作用域:代码块内,代码块:用{ }括起来的区域,就叫作代码块。

举例:
局部变量的出了作用域,变量将不能正常被访问
static,const,volatile,extern,register关键字深入解析
全局变量的作用域是整个工程
static,const,volatile,extern,register关键字深入解析
当全局变量与局部变量命名冲突时优先使用局部变量
static,const,volatile,extern,register关键字深入解析

2.STM32工程中头文件的作用

在一个工程中应该有多个外设源文件,以及一个包含一个main函数的源文件,各位外设源文件实现的功能不一样(功能函数),肯定避免不了各个外设源文件相互调用函数情况,此时我们只需要每个源文件对应一个外设头文件。
而头文件包含:
1.其他外设头文件和系统头文件
2.所有全局变量的声明
3.所有的函数声明
4.#define,结构体声明,枚举声明,typedef类型等等
当其他源文件想调用该外设源文件的内容时只要包含这个外设源文件对应头文件就OK了
头文件的作用:组织项目结构的时候,减少大型项目的维护成本

外设相关文件
stm32f10x.h这个文件是非常牛逼的啦
stm32f10x.h:实现了内核(CPU)之外的所有外设的寄存器映射,以及一些外设库函数的参数
stm32f10x_xx.c: 外设的驱动函数库文件
xx: GPIO、USRAT、I2C、SPI、FSMC…

这里我截取的一了部分
static,const,volatile,extern,register关键字深入解析

stm32f10x_xx.h: 存放外设的初始化结构体,外设初始化结构体成员的参数列表,外设固件库函数的声明
static,const,volatile,extern,register关键字深入解析

3.声明关键字extern

1.extern修饰全局变量
static,const,volatile,extern,register关键字深入解析
为了体现头文件的作用我把声明统统放到test.h这个头文件中
static,const,volatile,extern,register关键字深入解析
将声明放入头文件中,只要包含这个源文件的头文件就可以使用这些声明,根本原因是在预编译过程会将源文件包含的头文件的内容全部包含到该源文件中,详情请参考—》预编译指令

注意:
声明并不会开辟内存,只是一个告知作用,不能用声明来初始化全局变量的值。
static,const,volatile,extern,register关键字深入解析

2.extern声明函数
static,const,volatile,extern,register关键字深入解析

注意:声明函数前面的extern可以省略(不会报错),声明全局变量的前面extern不能省略。

static,const,volatile,extern,register关键字深入解析
虽然声明函数可以省略extern关键字,但建议不要省略,使代码更加严谨。

前面test.h头文件中忘记加条件编译防止头文件被重复包涵这里补上:具体原理请看–>预编译指令
**
static,const,volatile,extern,register关键字深入解析

三.static关键字三个作用(重点)

1.static修饰全局变量

static修饰全局变量:该变量的只在本文件内被访问,不能被外部其他直接访问。

原理:static修饰全局变量改变的是全局变量的作用域,作用域从整个工程变为本文件内,全局变量的生命周期并没有变。

static,const,volatile,extern,register关键字深入解析
虽然不能直接访问但是可以通过函数调用的方式进行间接访问
static,const,volatile,extern,register关键字深入解析

2.static修饰函数

static修饰函数,该函数只能在本文内被访问,不能在外部其他文件直接访问。

原理:static修饰函数也是改变的函数的作用域,作用域从整个工程变为本文件内。
static,const,volatile,extern,register关键字深入解析
static修饰函数,当然函数也可以间接被访问,不就在嵌套一个函数嘛。

static,const,volatile,extern,register关键字深入解析

3.static修饰局部变量

static修饰局部变量,则该局部变量存储在静态数据区,更改局部变量的生命周期从局部变量生命周期改成全局生命周期也就是说程序运行的整个生命周期内一直都有效,但作用域仍然不变还是一个代码块内

先看一段代码,局部变量不加static关键字修饰:
static,const,volatile,extern,register关键字深入解析
局部变量加static关键字修饰:

static,const,volatile,extern,register关键字深入解析
static修饰局部变量,局部变量的作用域仍然不变还是在一个代码块内

static,const,volatile,extern,register关键字深入解析
出了代码块,变量并不能被正常访问,static修饰局部变量你只能说局部变量活的更久了,就是没有被释放该变量的内存,一定要细细品味!!

为什么static修饰局部变量生命周期具有全局性:
static,const,volatile,extern,register关键字深入解析

根本原因是存储的位置发生了变化,被static修饰局部变量被存放在全局数据区。

还有一个问题局部变量为啥具有临时性,这里简单提一下叭
static,const,volatile,extern,register关键字深入解析
这里简单提一下关于函数栈帧还有很多细节,这里不是我们的重点,到后面会专门出一篇关于函数栈帧的详解,系统阐述具体原理。

四.只读关键字-const(重点)

1.const修饰变量

1.const修饰变量:变量不可以直接被修改

static,const,volatile,extern,register关键字深入解析

注意:
这里const并不会修饰关键字int,所以int的位置并不影响结果。
所以const int a 与int const a 是等价的 const修饰的都是变量a但习惯是第一种。

虽然不能直接修改,但可以通过指针操作间接修改变量的值:

static,const,volatile,extern,register关键字深入解析
对与用const修饰的变量,如果真正想改还是可以做到的,那问题来了const的意义是什么呢

如果我们用const去修饰一个变量肯定希望变量不应该被修改
1.让编译器进行直接修改式检查 ,当直接修改const修饰的变量时编译器报错提醒。
2. 告诉其他程序员(正在改你代码或者阅读你代码的)这个变量后面尽量不要改,改了可能会出问题。

const定义的常量不是标准意义上的常量,作为数组的元素个数
static,const,volatile,extern,register关键字深入解析
字符串真正意义不能被修改
static,const,volatile,extern,register关键字深入解析

2.const修饰数组

2.const修饰数组:数组的每个元素不能被直接修改
static,const,volatile,extern,register关键字深入解析
利用指针间接修改数组元素
static,const,volatile,extern,register关键字深入解析

3.const修饰指针

const关键字不能修饰关键字(int ),所以直接忽略int的位置,然后往const的右边看离谁近就修饰谁,这里只能修饰 * 操作符 或 指针 p。

  • 1.const int *p; //const修饰 * 操作符,p指针可变,p指向的对象(*p)不可变。

  • 2.int const *p; //const修饰 * 操作符,p指针可变,p指向的对象(*p)不可变。与上式一样

static,const,volatile,extern,register关键字深入解析

  • 3.int * const p; //const修饰 p 指针,p指针不可变,p指向的对象(*p)可变。
    static,const,volatile,extern,register关键字深入解析

  • 4.const int * const p; //前一个const修饰 *操作符,后一个const修饰 p 指针,指针p与指针指向的对象*p都不能变。
    static,const,volatile,extern,register关键字深入解析

4.const修饰函数参数

这里只是为了代码更加严谨,因为这个函数只是一个打印的函数,所以我们加一个const关键字修饰 参数的 *操作符使函数内并不能修饰我们的a变量的值。
static,const,volatile,extern,register关键字深入解析
static,const,volatile,extern,register关键字深入解析

5.const修饰函数的返回值

一般用来修饰返回的指针。
static,const,volatile,extern,register关键字深入解析

正确的方式应该接收的指针前面也加const修饰

static,const,volatile,extern,register关键字深入解析

五.最易变的关键字-volatile(重点)

1.理解volatile关键字

volatile是易变的,不稳定的意思。

要用volatile关键字修饰的变量,表示该变量可能被操作系统,硬件(例如单片机的外设),或者其他线程等更改,如果我们用volatile关键字去修饰这个变量,则编译器对访问该变量的的代码就不在进行优化(我自身对优化的理解:编译器可能会认为我们自己写的代码不会修改这个变量,则把该变量直接拿到CPU中,而之后CPU不会再去内存读取该变量,但殊不知虽然我们写的代码不会修改该变量,但硬件可能会对该变量进行修改,而修改之后的值是存放在内存之中,而CPU此时并不会去内存读取该变量的值,最后导致变量的值不能及时更新),可以提定对特殊地址的稳定访问。

接下来在linux环境下做个实验:

不加volatile:

static,const,volatile,extern,register关键字深入解析

static,const,volatile,extern,register关键字深入解析
总结:不加volatile关键字修饰pass这个变量则编译器(会认为pass这个变量不会被修改)可能会进行优化,之后CPU不会再从内存中读取pass变量的值,那么问题来了如果pass的值此时被操作系统,或者硬件将pass的值改成0,按理说CPU应该退出循环,但此时CPU并不会去内存读取pass的值(0),则CPU并不会退出循环那就出现了问题!!

加加volatile:

static,const,volatile,extern,register关键字深入解析
总结:加上volatile修饰变量,此时会忽略编译器的优化,也就是说CPU会一直向内存读取的pass的值再进行判定是否执行循环,此时就算pass的值被硬件改成0,则CPU也可以读取到然后终止循环

2.volatile关键字在STM32中的运用

你仔细观察你会发现基本上SMT32中定义的所有寄存器都前面都加上了
volatile关键字来进行修饰。

static,const,volatile,extern,register关键字深入解析

每个结构体成员前增加了一个“__IO”前缀,它的原型是volatile关键字,这些结构体内的成员,都代表着寄存器,而寄存器(在内存当中)很多时候是由硬件(外设或 STM32 芯片状态)修改的,也就是说即使 CPU 不执行代码修改这些变量,变量的值也有可能被外设修改、更新,所以每次使用这些变量的时候,我们都要求CPU 去该变量的地址重新读取这些寄存器。 若没有这个关键字修饰,在某些情况下,编译器认为没有代码修改该变量,就直接从 CPU 的某个缓存(CPU内部的寄存器或高速缓存)获取该变量值,这时虽然可以加快执行速度,但该缓存中的是陈旧数据,与我们要求的寄存器最新状态可能会有出入。

总结:volatile关键字叫编译器不要进行优化,也就是说让CPU每次都老老实实去内存中读取的寄存器中的值,慢点就慢点但是可以保证CPU读取的寄存器的值都是最新的!!!

结尾

上面加了重点二字面试极有可能考到,基本上是C语言中最重要的几个关键字,运用比较广泛,我们必须理解并掌握它,如果觉得本文对有有小小的帮助就快快点赞收藏叭!!!!文章来源地址https://www.toymoban.com/news/detail-404719.html

到了这里,关于static,const,volatile,extern,register关键字深入解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式C语言关键字(const、static、volitatile)

    C语言中const修饰通常是用来声明常量,并声明常量的值不能修改。当涉及 指针变量 时情况就会变得更加有趣,需要特别注意。因为有两样东西都有可能成为常量— 一种是用来限定指向的空间的值不可修改;另一种是限定指针不可修改 。下面是几个声明的例子: 总结:cons

    2024年02月06日
    浏览(42)
  • 在C++和C中static关键字的用法,在C++和C中const关键字的用法

    1、在C++和C中static的用法 答:static表示存储类型,修饰数据类型。在C语言中,static修饰局部变量,内存分配在静态区,生命周期延长,作用域不变。static修饰全局变量,内存分配在静态区,作用域被局限于本文件,不能被extern引用。static修饰函数,不能被exter

    2024年02月10日
    浏览(37)
  • C++面试八股文:static和const的关键字有哪些用法?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第7面: 面试官:C++中, static 和 const 的有哪些用法? 二师兄: satic 主要用在以下三个方面:1.用在全局作用域,修饰的变量或者函数为静态的,限制在本文件内使用。2.方法内修饰修饰静态局部变量,在第一次访问

    2024年02月08日
    浏览(47)
  • 关于c++中mutable、const、volatile这三个关键字及对应c++与汇编示例源码

    这哥三之间的关系是有趣的,不妨看看这个: cv (const and volatile) type qualifiers - cppreference.com permits modification of the class member declared mutable even if the containing object is declared const. 即便一个对象是const的,它内部的成员变量如果被mutable修饰,则此成员变量依旧可以被修改。 很常见,

    2024年02月13日
    浏览(37)
  • volatile关键字作用

    volatile是一个和多线程相关的,主要有一下2点作用(只保证可见性,不保证原子性) 防止指令重排(有序性) JVM在不改变程序执行结果的前提下,在编译时会对指令的顺序进行重新排序,而volatile则能够禁止指令的重新排序 能够确保线程内存中的对象对其他内存可

    2024年02月15日
    浏览(45)
  • volatile 关键字详解

    目录 volatile volatile 关键用在什么场景下: volatile 防止编译器优化: volatile   是一个在许多编程语言中(包括C和C++)用作的标识符。它用于告诉编译器不要对带有该修饰的变量进行优化,以确保变量在特定情况下的可见性和预测性。 在C和C++中, volatile

    2024年02月11日
    浏览(41)
  • [JAVAee]volatile关键字

    目录 1.volatile的特性 ①保持线程可见性 2.volatile注意事项及适用场景 ①注意事项 ②适用场景 volatile,译为\\\"易变的\\\". 对此我们就可以这样理解,对于被volatile修饰的变量的数值,是容易变化的. 在之前的线程安全文章中,我们有讲解过\\\"可见性\\\",对于线程间的这个特性可能会导致:线程

    2024年02月16日
    浏览(36)
  • 【C】volatile 关键字

    1)基本概念 const 是C语言的一个。 const 用于告诉编译器相应的变量可能会在程序的控制之外被修改,因此编译器不应该对其进行优化。 声明语法: 作用: 防止编译器优化,确保对变量的每次访问都是实际的读写操作,而不是使用缓存中的值。 用于表示可能会 被异步

    2024年01月22日
    浏览(42)
  • JAVA volatile 关键字

    volatile 是JAVA虚拟机提供的轻量级的同步机制,有三大特性 1、保证可见性  2、不保证原子性  3、禁止指令重排 JMM  JAVA内存模型本身是一种抽象的概念并不真实存在 它描述的是一组规则或规范,提供这组规范定义了程序中各个变量(包括实例变量、静态变量)的访问方式。

    2024年02月13日
    浏览(50)
  • 【多线程】volatile关键字

    一、volatile 1.volatile的底层原理是内存屏障,Memory Barrier, Memory Fence 2.对volatile变量的写指令(赋值操作)后会加入写屏障 3.对volatile变量的读指令(取变量值)前会加入读屏障 4.写屏障的作用会将写屏障之前的赋值改动操作,对共享变量的改动都同步到主内存中 5.读屏障的作

    2024年02月06日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包