精华内容
下载资源
问答
  • 变量的生命周期7.链接属性8.最后的总结第二部分、章节介绍2.1.概念解析本节详细介绍了存储类、作用域、生命周期、链接属性这几个概念。2.2.linux下C程序的内存映像本节讲述linux下C语言程序的内存映像,进一步提出...

    第一部分、章节目录

    1.概念解析

    2.linuxC程序的内存映像

    3.存储类相关的关键字1

    4.存储类相关的关键字2

    5.作用域详解

    6.变量的生命周期

    7.链接属性

    8.最后的总结

    第二部分、章节介绍

    2.1.概念解析

    本节详细介绍了存储类、作用域、生命周期、链接属性这几个概念。

    2.2.linuxC程序的内存映像

    本节讲述linuxC语言程序的内存映像,进一步提出代码段、数据段、bss段、栈、堆的概念,方便后面内容的讲述

    2.3、存储类相关的关键字1

    本节讲述存储类关键字autostaticregister,其中重点是static

    2.4.存储类相关的关键字2

    本节讲述存储类关键字externvolatilerestricttypedef,其中重点是externvolatile

    2.5.作用域详解

    本节详细讲述作用域,分析了各种不同属性的变量的作用域规则。

    2.6.变量的生命周期

    本节重点分析变量的生命周期,从该角度并联系前面的概念对各种变量进行了分析。

    2.7.链接属性

    本节详细讲解C语言的三种链接属性:外连接、内链接、无连接,重点解释了static的第二种用法。

    2.8.最后的总结

    本节对前面7节讲述的内容进行了综合和总结,通过总结希望大家彻底联系上面几节的分知识点,能从整体上认识C语言的工作规则。

    第三部分、随堂记录

    3.1.概念解析

    3.1.1、存储类

    (1)存储类就是存储类型,也就是描述C语言变量在何种地方存储。

    (2)内存有多种管理方法:栈、堆、数据段、bss段、.text段······一个变量的存储类属性就是描述这个变量存储在何种内存段中。

    (3)譬如:局部变量分配在栈上,所以它的存储类就是栈;显式初始化为非0的全局变量分配在数据段,显式初始化为0和没有显示初始化(默认为0)的全局变量分配在bss段。

    3.1.2、作用域

    (1)作用域是描述这个变量起作用的代码范围。

    (2)基本来说,C语言变量的作用域规则是代码块作用域。意思就是这个变量起作用的范围是当前的代码块。代码块就是一对大括号{}括起来的范围,所以一个变量的作用域是:这个变量定义所在的{}范围内从这个变量定义开始往后的部分。(这就解释了为什么变量定义总是在一个函数的最前面)

    eaeacde1172a170b59d87178d6c83acf.png

    0f6762c16c27c838309e3784bc5ce616.png

    42def8e482ac0ff26f05f1dd7e6756e6.png

    3.1.3、生命周期

    (1)声明周期是描述这个变量什么时候诞生(运行时分配内存空间给这个变量)及什么时候死亡(运行时收回这个内存空间,此后再不能访问这个内存地址,或者访问这个内存地址已经和这个变量无关了)的。

    (2)变量和内存的关系,就和人(变量)去图书馆借书(内存)一样。变量的生命周期就好象我人借书的这段周期一样。

    (3)研究变量的生命周期可以我们理解程序运行的一些现象、理解C语言的一些规则。

    3.1.4、链接属性

    (1)大家知道程序从源代码到最终可执行程序,经历的过程:编译、链接。

    (2)编译阶段就是把源代码搞成.o目标文件,目标文件里面有很多符号和代码段、数据段、bss段等分段。符号就是编程中的变量名、函数名等。运行时变量名、函数名能够和相应的内存对应起来,靠符号来做链接的。

    (3).o的目标文件链接生成最终可执行程序的时候,其实就是把符号和相对应的段给链接起来。

    C语言中的符号有三种链接属性:外连接属性、内链接属性、无连接属性。

    总结:以上4个概念,其实就是从4个不同角度来分析C语言的一些运行规则。综合这4种分析角度能够让程序员完全掌握C语言程序的运行规则和方法。

    3.2.linuxC程序的内存映像

    3.2.1、代码段、只读数据段

    (1)对应着程序中的代码(函数),代码段在linux中又叫文本段(.text)

    (2)只读数据段就是在程序运行期间只能读不能写的数据,const修饰的常量有可能是存在只读数据段的(但是不一定,const常量的实现方法在不同平台是不一样的)

    3.2.2、数据段、bss

    (1)数据段存:1、显式初始化为非0的全局变量;2、显式初始化为非0static局部变量

    (2)bss段存:1、显式初始化为0或者未显式初始化的全局变量;2、显式初始化为0或未显式初始化的static局部变量。

    3.2.3、堆

    C语言中什么样变量存在堆内存中?C语言不会自动向堆中存放东西,堆的操作是程序员自己手工操作的。程序员根据需求自己判断要不要使用堆内存,用的时候自己申请,自己使用,完了自己释放。

    3.2.4、文件映射区

    文件映射区就是进程打开了文件后,将这个文件的内容从硬盘读到进程的文件映射区,以后就直接在内存中操作这个文件,读写完了后在保存时再将内存中的文件写到硬盘中去。

    3.2.5、栈

    栈内存区,局部变量分配在栈上;函数调用传参过程也会用到栈

    3.2.6、内核映射区

    (1)内核映射区就是将操作系统内核程序映射到这个区域了。

    (2)对于linux中的每一个进程来说,它都以为整个系统中只有它自己和内核而已。它认为内存地址0xC0000000以下都是它自己的活动空间,0xC0000000以上是OS内核的活动空间。

    (3)每一个进程都活在自己独立的进程空间中,0-3G的空间每一个进程是不同的(因为用了虚拟地址技术),但是内核是唯一的。

    3.2.7OS下和裸机下C程序加载执行的差异

    (1)C语言程序运行时环境有一定要求,意思是单独个人写的C语言程序没法直接在内存中运行,需要外部一定的协助,这段协助的代码叫加载运行代码(或者叫构建C运行时环境的代码,这一段代码在操作系统下是别人写好的,会自动添加到我们写的程序上,这段代码的主要作用是:给全局变量赋值、清bss段)。

    (2)ARM裸机第十六部分,写shell时有一次定义了一个全局变量初始化为0但是实际不为0,后来在裸机的start.S中加了清bss段代码就变0了。这就说明在裸机程序中没人帮我们来做这一段加载运行时代码,要程序员自己做(start.S中的重定位和清bss段就是在做这个事);在操作系统中运行程序时程序员自己不用操心,会自动完成重定位和清bss,所以我们看到的现象:C语言中未初始化的全局变量默认为0·····。

    (3)数据段的全局变量或静态局部变量都是有非0的初值的,这些初值在main函数运行之前就已经被初始化了,是重定位期间完成的初始化。

    3.3、存储类相关的关键字1

    3.3.1auto

    (1)auto关键字在C语言中只有一个作用,那就是修饰局部变量。

    (2)auto修饰局部变量,表示这个局部变量是自动局部变量,自动局部变量分配在栈上。(既然在栈上,说明它如果不初始化那么值就是随机的······)

    (3)平时定义局部变量时就是定义的auto的,只是省略了auto关键字而已。可见,auto的局部变量其实就是默认定义的普通的局部变量。

    3.3.2static

    (1)static关键字在C语言中有2种用法,而且这两种用法彼此没有任何关联、完全是独立的。其实当年本应该多发明一个关键字,但是C语言的作者觉得关键字太多不好,于是给static增加了一种用法,导致static一个关键字竟然有两种截然不同的含义。

    (2)static的第一种用法是:用来修饰局部变量,形成静态局部变量。要搞清楚静态局部变量和非静态局部变量的区别。本质区别是存储类不同(存储类不同就衍生出很多不同):非静态局部变量分配在栈上,而静态局部变量分配在数据段/bss段上。

    (3)static的第二种用法是:用来修饰全局变量,形成静态全局变量。要搞清楚静态全局变量和非静态全局变量的区别。区别是在链接属性上不同,讲到链接属性时详细讲。

    分析:

    1、静态局部变量在存储类方面和全局变量一样。

    2、静态局部变量在生命周期方面和全局变量一样。

    3、静态局部变量和全局变量的区别是:作用域、连接属性。静态局部变量作用域是代码块作用域(和普通局部变量是一样的)、链接属性是无连接;全局变量作用域是文件作用域(和函数是一样的)、链接属性方面是外连接。

    3.3.3register

    (1)register关键字不常用,也只有一个作用,那就是:register修饰的变量。编译器会尽量将它分配在寄存器中。(平时分配的一般的变量都是在内存中的)。分配在寄存器中一样的用,但是读写效率会高很多。所以register修饰的变量用在那种变量被反复高频率的使用,通过改善这个变量的访问效率可以极大的提升程序运行效率时。所以register是一种极致提升程序运行效率的手段。

    (2)uboot中用到了一个register类型的变量,gd这个变量是用来存uboot的全局变量(gd就是global data)。因为这个全局变量在整个uboot中到处都被访问,所以定义成register的。

    (3)平时写代码要被定义成register这种情况很少,一般慎用。

    (4)register编译器只能承诺尽量将register修饰的变量放在寄存器中,但是不保证一定放在寄存器中。主要原因是因为寄存器数量有限,不一定有空用。

    3.4.存储类相关的关键字2

    3.4.1extern

    (1)extern主要用来声明全局变量,声明的目的主要是在a.c中定义全局变量而在b.c中使用该变量。

    (2)C语言中程序的编译时以单个.c源文件为单位的,因此编译a.c时只考虑a.c中的内容(不会考了b.c的内容),这就导致a.c中使用了b.c中定义的变量时在编译时报错。解决方案是声明

    (3)应该在a.c中使用g_b之前先声明g_b,声明就是告诉a.c我在别的文件中定义了g_b,并且它的原型和声明的一样,将来在链接的时候链接器会在别的.o文件中找到这个同名变量。声明一个全局变量就要用到extern关键字

    A.c

    int global_a;

    B. c

    bc2ae5b634f9e88399f542dac6375c04.png

    6bca148fae8935bc7f697cf70639b861.png

    3.4.2volatile

    (1)volatile的字面意思:可变的、易变的。C语言中volatile用来修饰一个变量,表示这个变量可以被编译器之外的东西改变。编译器之内的意思是变量的值的改变是代码的作用,编译器之外的改变就是这个改变不是代码造成的,或者不是当前代码造成的,编译器在编译当前代码时无法预知。譬如在中断处理程序isr中更改了这个变量的值,譬如多线程中在别的线程更改了这个变量的值,譬如硬件自动更改了这个变量的值(一般这个变量是一个寄存器的值)

    (2)以上说的三种情况(中断isr中引用的变量,多线程中共用的变量,硬件会更改的变量)都是编译器在编译时无法预知的更改,此时应用使用volatile告诉编译器这个变量属于这种(可变的、易变的)情况。编译器在遇到volatile修饰的变量时就不会对改变量的访问进行优化,就不会出现错误。

    (3)编译器的优化在一般情况下非常好,可以帮助提升程序效率。但是在特殊情况(volatile)下,变量会被编译器想象之外的力量所改变,此时如果编译器没有意识到而去优化则就会造成优化错误,优化错误就会带来执行时错误。而且这种错误很难被发现。

    (4)volatile是程序员意识到需要volatile然后在定义变量时加上volatile,如果你遇到了应该加volatile的情况而没有加程序可能会被错误的优化。如果在不应该加volatile而加了的情况程序不会出错只是会降低效率。所以我们对于volatile的态度应该是:正确区分,该加的时候加不该加的时候不加,如果不能确定该不该加为了保险起见就加上。

    3.4.3restrict

    (1)c99中才支持的,所以很多延续c89的编译器是不支持restrict关键字,gcc支持的。

    (2)restrict也是和编译器行为特征有关的。

    (3)restrict只用来修饰指针,不能修饰普通变量。

    (4)http://blog.chinaunix.net/uid-22197900-id-359209.html

    (5)memcpymemmove的区别

    3.4.4typedef

    (1)之前讲过了

    (2)typedefC语言关键字归类上属于存储类关键字,但是实际上和存储类没关系。

    3.5.作用域详解

    3.5.1、局部变量的代码块作用域

    (1)代码块基本可以理解为一对大括号{}括起来的部分。

    (2)代码块不等于函数,因为if  while for都有{}。所以代码块<=函数

    (3)局部变量的作用域是代码块作用域,也就是说一个局部变量可以被访问和使用的范围仅限于定义这个局部变量的代码块中定义式之后的部分。

    da4018b688a8c8fcb5bf8707f32787f4.png

    3.5.2、函数名和全局变量的文件作用域

    (1)文件作用域的意思就是全局的访问权限,也就是说整个.c文件中都可以访问这些东西。这就是平时所说的局部和全局,全局就是文件作用域。

    (2)详细准确的说:函数和全局变量的作用域是定义所在的整个.c文件之内定义式之后的部分。

    总结:

    (1)不管是局部变量、全局变量、函数,都要先定义才能使用

    (2)严格来说我们上面的总结是错误的。准确的说:全局变量/函数的作用域都是自己所在的文件,但是定义式之前的部分因为缺少声明所以没法用,解决方案是:1、把它定义到前面去;2、定义到后面但是在前面加声明;局部变量因为没法声明,所以只能定义在前面去。

    (3)c89标准的编译器中(现在很多编译器还延续使用c89标准),所有的局部变量必须先定义在最前面,在变量定义之前不能有一句执行代码。在c99标准的编译器中(gcc兼容c99标准)可以允许在代码块内任意地方定义变量。但是允许定义的变量还是只能使用在定义了之后,定义之前还是不能用的。

    3.5.3、同名变量的掩蔽规则

    (1)问题:编程时,不可避免会出现同名变量。变量同名后不一定会出错。

    (2)首先,如果两个同名变量作用域不同且没有交叠,这种情况下同名没有任何影响。

    (3)其次,如果两个同名变量作用域有交叠,C语言规定在作用域交叠范围内,作用域小的一个变量会掩蔽掉作用域大的那个(县官不如现管)。

    3.6.变量的生命周期

    3.6.1、研究变量生命周期的意义

    (1)研究变量生命周期,有助于理解变量的行为特征。

    3.6.2、栈变量的生命周期

    (1)局部变量(栈变量)存储在栈上,生命周期是临时的。临时的意思就是说:代码执行过程中按照需要去创建、使用、消亡的。

    (2)譬如一个函数内定义的局部变量,在这个函数每一次被调用时都会创建一次,然后使用,最后在函数返回的时候消亡。

    (3)思考:一个函数内的局部变量为什么在函数外不能使用?

    (4)思考:局部变量为什么分配在栈上?或者说局部变量为什么是临时生命周期?

    3.6.3、堆变量的生命周期

    (1)首先要明白:堆内存空间是客观存在的,是由操作系统维护的。我们程序只是去申请然后使用然后释放。

    (2)我们只关心我们程序使用堆内存的这一段时间,因此堆变量也有了自己的生命周期,就是:从malloc申请时诞生,然后使用,直到free时消亡。

    (3)所以堆内存在malloc之前和free之后不能再去访问,因此堆内存在实践编程时都是被反复的mallocfree的。

    3.6.4、数据段、bss段变量的生命周期

    (1)全局变量的生命周期是永久的。永久的意思就是在程序被执行时诞生,在程序终止时消亡。

    (2)全局变量所占用的内存是不能被程序自己释放的,所以程序如果申请了过多的全局变量会导致这个程序一直占用大量内存。

    (3)如果说堆内存是图书馆借的书,那么全局变量就是自己买的书。

    3.6.5、代码段、只读段的生命周期

    (1)其实就是程序执行的代码,其实就是函数,它的生命周期是永久的。不过一般代码的生命周期我们并不关注。

    (2)有时候放在代码段的不只是代码,还有const类型的常量,还有字符串常量。(const类型的常量、字符串常量有时候放在rodata段,有时候放在代码段,取决于平台)

    3.7.链接属性

    3.7.1C语言程序的组织架构:多个C文件+多个h文件

    (1)庞大、完整的一个C语言程序(譬如linux内核、uboot)由多个c文件和多个h文件组成的。

    (2)程序的生成过程就是:编译+链接。编译是为了将函数/变量等变成.o二进制的机器码格式,链接是为了将各个独立分开的二进制的函数链接起来形成一个整体的二进制可执行程序。

    3.7.2、编译以文件为单位、链接以工程为单位

    (1)编译器工作时是将所有源文件依次读进来,单个为单位进行编译的。

    (2)链接的时候实际上是把第一步编译生成个单个的.o文件整体的输入,然后处理链接成一个可执行程序。

    3.7.3、三种链接属性:外连接、内链接、无链接

    (1)外连接的意思就是外部链接属性,也就是说这家伙可以在整个程序范围内(言下之意就是可以跨文件)进行链接,譬如普通的函数和全局变量属于外连接。

    (2)内链接的意思就是(c文件内部)内部链接属性,也就是说这家伙可以在当前c文件内部范围内进行链接(言下之意就是不能在当前c文件外面的其他c文件中进行访问、链接)。static修饰的函数/全局变量属于内链接。

    (3)无连接的意思就是这个符号本身不参与链接,它跟链接没关系。所有的局部变量(auto的、static的)都是无连接的

    3.7.4、函数和全局变量的同名冲突

    (1)因为函数和全局变量是外部链接属性,就是说每一个函数和全局变量将来在整个程序中所有的c文件都能被访问,因此在一个程序中的所有c文件中不能出现同名的函数/同名的全局变量。

    (2)最简单的解决方案就是起名字不要重复,但是很难做到。主要原因是一个很大的工程中函数和全局变量名字太多了,而且一个大工程不是一个人完成的,是很多人协作完成,所以很难保证不会重名。解决方案呢?

    (3)现代高级语言中完美解决这个问题的方法是命名空间namespace(其实就是给一个变量带上各个级别的前缀)。但是C语言不是这么解决的。

    (4)C语言比较早碰到这个问题,当时还没发明namespace概念,当时C语言就发明了一种不是很完美但是凑活能用的解决方案,就是三种链接属性的方法。

    (5)C语言的链接属性解决重名问题思路是这样的:我们将明显不会在其他c文件中引用(只在当前c文件中引用)的函数/全局变量,使用static修饰使其成为内链接属性,这样在将来连接时即使2c文件中有重名的函数/全局变量,只要其中一个或2个为内链接属性就没事。

    (6)这种解决方案在一定程度上解决了问题。但是没有从根本上解决问题,留下了很多麻烦。所以这个就导致了C语言写很大型的项目难度很大。

    3.7.5static的第二种用法:修饰全局变量和函数

    (1)普通的(非静态)的函数/全局变量,默认的链接属性是外部的

    (2)static(静态)的函数/全局变量,链接属性是内部链接。

    3.7.6、一般用法总结:

    思考:为什么static一个关键字可以有2种完全不同的意思?因为这两种用法是互斥的。

    3.8.最后的总结

    (1)普通(自动)局部变量分配在栈上,作用域为代码块作用域,生命周期是临时,连接属性为无连接。定义时如果未显式初始化则其值随机,变量地址由运行时在栈上分配得到,多次执行时地址不一定相同,函数不能返回该类变量的地址(指针)作为返回值。

    (2)静态局部变量分配在数据段/bss段(显式初始化为非0则在数据段,显式初始化为0或未显示初始化则在bss段),作用域为代码块作用域(人为规定的),生命周期为永久(天然的),链接属性为无连接(天然的)。定义时如果未显式初始化则其值为0(天然的),变量地址由运行时环境在加载程序时确定,整个程序运行过程中唯一不变;静态局部变量其实就是作用域为代码块作用域(同时链接属性为无连接)的全局变量。静态局部变量可以改为用全局变量实现(程序中尽量避免用全局变量,因为会破坏结构性)。

    (3)静态全局变量/静态函数和普通全局变量/普通函数的唯一差别是:static使全局变量/函数的链接属性由外部链接(整个程序所有文件范围)转为内部链接(当前c文件内)。这是为了解决全局变量/函数的重名问题(C语言没有命名空间namespace的概念,因此在程序中文件变多之后全局变量/函数的重名问题非常严重,将不必要被其他文件引用的全局变量/函数声明为static可以很大程度上改善重名问题,但是仍未彻底解决)。

    (4)写程序尽量避免使用全局变量,尤其是非static类型的全局变量。能确定不会被其他文件引用的全局变量一定要static修饰。

    (5)注意区分全局变量的定义和声明。一般规律如下:如果定义的同时有初始化则一定会被认为是定义;如果只是定义而没有初始化则有可能被编译器认为是定义,也可能被认为是声明,要具体分析;如果使用extern则肯定会被认为是声明(实际上使用extern也可以有定义,实际上加extern就是明确声明这个变量为外部链接属性)。

    (6)全局变量应该定义在c文件中并且在头文件中声明,而不要定义在头文件中(因为如果定义在头文件中,则该头文件被多个c文件包含时该全局变量会重复定义)。

    (7)b.c中引用a.c中定义的全局变量/函数有2种方法:一是在a.h中声明该函数/全局变量,然后在b.c#include ;二是在b.c中使用extern显式声明要引用的函数/全局变量。其中第一种方法比较正式。

    (8)存储类决定生命周期,作用域决定链接属性

    (9)宏和inline函数的链接属性为无连接。

    展开全文
  • 静态变量的作用域是当前源文件,全局变量的作用域是整个可执行程序。 值得注意的是: 如果在头文件定义全局变量,在预编译期间#include的头文件会被拷贝进源文件中,编译器是不知道头文件的。 虽然全局变量是...

    全局变量和静态变量的存储方式是一样的,只是作用域不同。如果它们未初始化或初始化为0则会存储在BSS段,如果初始化为非0值则会存储在DATA段,见进程的地址空间分配一文。 静态变量的作用域是当前源文件,全局变量的作用域是整个可执行程序。 值得注意的是:

    • 如果在头文件定义全局变量,在预编译期间#include的头文件会被拷贝进源文件中,编译器是不知道头文件的。
    • 虽然全局变量是全局作用域,但需要extern关键字来声明以通过编译。因为C++是强类型语言,编译时需要根据变量声明做类型检查。

     

    原文链接:

    http://harttle.com/2015/09/30/cpp-static-and-global.html

    转载于:https://www.cnblogs.com/qingyuanjushi/p/5750648.html

    展开全文
  • 静态变量的作用域是当前源文件,全局变量的作用域是整个可执行程序。 值得注意的是:如果在头文件定义全局变量,在预编译期间#include的头文件会被拷贝进源文件中,编译器是不知道头文件的。虽然全局变量是全局作用...

    全局变量和静态变量的存储方式是一样的,只是作用域不同。如果它们未初始化或初始化为0则会存储在BSS段,如果初始化为非0值则会存储在DATA段,见进程的地址空间分配一文。静态变量的作用域是当前源文件,全局变量的作用域是整个可执行程序。 值得注意的是:

    • 如果在头文件定义全局变量,在预编译期间#include的头文件会被拷贝进源文件中,编译器是不知道头文件的。
    • 虽然全局变量是全局作用域,但需要extern关键字来声明以通过编译。因为C++是强类型语言,编译时需要根据变量声明做类型检查。

    全局变量的引用

    C++源文件中引用外部定义的全局变量和引用外部函数是一样的语法,通过extern来声明:

    // file: a.cpp
    #include<iostream>
    extern int a;
    int main() {
        std::cout<<b<<std::endl;
        return 0;
    }
    
    // file: b.cpp
    #include<iostream>
    int a = 2;
    

    然后分别编译这两个文件,链接生成a.out并执行它:

    $ g++ a.cpp b.cpp
    $ ./a.out
    b.cpp
    2
    

    extern只是在当前文件中声明有这样一个外部变量而已,并不指定它来自哪个外部文件。所以即使extern变量名错误当前源文件也能通过编译,但链接会出错。

    头文件中定义

    因为头文件可能会被多次引用,在预编译时被引用的头文件会被直接拷贝到源文件中再进行编译。一个常见的错误便是把变量定义放在头文件中,例如下面的变量int a

    // file: a.cpp
    #include <iostream>
    #include "b.h"
    int main() {
        std::cout<<a<<std::endl;
        return 0;
    }
    
    // file: b.cpp
    #include<iostream>
    #include"b.h"
    void f(){}
    
    // file: b.h
    int a = 2;
    

    头文件b.h中定义了int a,它被a.cppb.cpp同时引入。我们将a.cppb.cpp分别编译是没有问题的,然后链接时会抛出错误:

    duplicate symbol _a in:
        /tmp/ccqpfU5e.o
        /tmp/ccCRi9nO.o
    ld: 1 duplicate symbol for architecture x86_64
    collect2: error: ld returned 1 exit status
    

    两个.o文件中的_a名称发生了冗余,这是变量重定义错误。

    头文件中声明

    因为声明操作是幂等的,而多次定义会引发重定义错误。所以 头文件中不应包含任何形式的定义,只应该包含声明,正确的办法是变量定义总是在源文件中进行,而声明放在头文件中:

    #include <iostream>
    #include "b.h"
    int main() {
        std::cout<<a<<std::endl;
        return 0;
    }
    
    // file: b.cpp
    #include<iostream>
    #include"b.h"
    int a = 2;
    
    // file: b.h
    extern a;
    

    然后编译链接执行都会通过,输出2

    $ g++ a.cpp b.cpp
    $ ./a.out
    2
    

    编译器看到g++ a.cpp b.cpp时会自动去查找b.h并进行预编译操作,因此不需要显式指定b.h

    静态全局变量

    非静态全局变量是外部可链接的(external linkage),目标文件中会为它生产一个名称供链接器使用;而静态全局变量是内部可链接的(internal linkage),目标文件中没有为链接器提供名称。因此无法链接到其他文件中,因此静态变量的作用域在当前源文件(目标文件)。虽然静态和非静态全局变量可能存储在同一内存块,但它们的作用域是不同的。 来看例子:

    // file: a.cpp
    #include <iostream>
    extern int a;
    int main() {
        std::cout<<a<<std::endl;
        return 0;
    }
    
    // file: b.cpp
    static int a = 2;
    

    然后g++ a.cpp b.cpp时发生链接错:

    Undefined symbols for architecture x86_64:
      "_a", referenced from:
          _main in ccPLYjyx.o
    ld: symbol(s) not found for architecture x86_64
    collect2: error: ld returned 1 exit status
    

    链接时未找到名称_a,因此静态变量在编译得到的目标文件中没有为链接器提供名称。所以其他目标文件无法访问该变量,静态全局变量的作用域是当前源文件(或目标文件)。

    全局变量初始化

    全局变量比较特殊,初始化有两种方式:

    • 静态初始化(static initialization):对于定义时给出初始化参数的全局变量,其初始化在程序加载时完成。根据是否被初始化、是否被初始化为0会把它存储在BSS或者DATA段中,参见进程的地址空间分配
    • 动态初始化(dynamic initialization):定义变量时可以不给出初始化参数,而是在某个函数中进行全局变量初始化。

    对于静态初始化,看这个例子:

    class C{
    public:
        C(){ std::cout<<"init "; }
    };
    C c;
    int main() { std::cout<<"main"; return 0; }
    

    main()进入之前,程序加载时动态初始化,程序输出为一行init main

    关于全局变量的初始化顺序,同一源文件中的全局变量初始化顺序按照定义顺序,不同源文件(编译单元)的全局变量初始化顺序并未规定。因此软件设计时不要依赖于其他编译单元的静态变量,可以通过单例模式来避免这一点。


    原文地址https://harttle.land/2015/09/30/cpp-static-and-global.html

    展开全文
  • 这两者的区别:非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序...

    静态全局变量和非静态全局变量的区别

    在全局变量的说明之前再加以static 就构成了静态的全局变量。
    全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。
    这两者的区别:非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
    从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。

    展开全文
  • Java变量的作用域静态变量、全局变量和局部变量变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。根据作用域的不同,一般将变量分为不同的...
  • 这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同...
  • 1.之前我理解类是为了节省时间演化来,因此里面的静态属性应该和模块中的全局变量作用范围类似,那类中的静态属性应该在整个类中都能访问。但实际为代码段所示。 2. ``` 2.1 全局变量能被内部定义函数所...
  • 变量的作用范围 取决于变量的定义位置 1.局部变量(动态储存) 1、仅能在它所定义的语句块内有效 例如: 第一个x,为零的作用域为定义它的语句块作用域(红色格格),...全局变量静态储存) 从此程序开始到此程...
  • 全局变量和静态全局变量

    千次阅读 2019-04-15 00:01:34
    这两者在存储方式上并无不同,区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的...
  • 1.这两者的区别虽在于非静态全局 变量的作用域是整个源程序, 当...2.由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此 可以避免在其它源文件中引起错误。 3.全局变量(外部变量)的说明
  • Java静态全局变量全局变量的区别

    千次阅读 2019-01-16 09:58:55
    Static全局变量与普通的...这两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个原文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量...
  • 也就是说,在Python中变量的作用域是由它在源代码中的位置决定的,这与C有些相似,但是Python与C在作用域方面的差异还是非常明显的。接下来会谈论Python的作用域规则,在这中间也会说明一下Python与C在作用域方...
  • 局部变量,全局变量静态变量的作用域,生命期? C++变量根据定义位置的不同,具有不同的作用域,作用域可分为6种:全局作用域,局部作用域,语句作用域,类作用域,命名作用域和文件作用域。 从作用域看: 全局...
  •  今天在论坛上看到有朋友发帖问道:既然静态全局变量与全局...也许答案非常简单:C++就是这么规定的,静态全局变量全局变量的唯一区别就是作用域不同。  对一个C++变量来说,有两个属性非常重要:作用域和生命
  • 静态全局变量

    2018-05-12 13:59:27
    通常情况下,静态全局变量的声明和定义放在源文件中,并且不能使用extern关键字将静态全局变量导出,因此静态全局变量的作用域仅限于定义静态全局变量所在的文件内部。 普通全局变量的作用域是整个工程,在头文件...
  • Java教程变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。 本篇文章由泉州SEOwww.234yp.com 整理发布,Java教程...
  • 这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在...
  • 这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中 都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, ...
  • 由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。 static全局变量只初使化一次,防止在其他文件单元中被引用。 3、局部变量: 存储区是栈区,离开...
  • C++ 变量根据定义的位置的不同的生命周期,具有不同的作用域,作用域可分为 6 种:全局作用域,局部作用域,语句作用域,类作用域,命名空间作用域和文件作用域。 从作用域看: 全局变量具有全局作用域。全局变量只...
  • 对于初学者来说,变量的作用域是它们所驻留的页面。因此,如果你定义了$var,页面余下部分就可以访问 $var,但是,其它页面一般不能访问它(除非使用特殊的变量)。因为包含文件像它们是原始(包含)脚本的一部分那样...
  • Java教程变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。根据作用域的不同,一般将变量分为不同的类型:成员变量和局部变量。下面对这几种...
  • 普通全局变量与静态全局变量 在全局变量... 这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作...
  • 普通局部变量分配在栈上,作用域为代码块作用域,生命周期是临时,链接属性是无连接(编译时是一个函数形成一个单元,链接时是函数与函数之间层次在交换,函数内部局部变量与其他函数没有直接联系,所以是无...
  • 变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。 根据作用域的不同,一般将变量分为不同的类型:成员变量和局部变量。 成员变量 Java 的...
  • 这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,645
精华内容 1,458
关键字:

静态全局变量的作用域