C++学习记录———extern,static,声明与定义,全局变量与局部变量

  • C++ 变量作用域

作用域是程序的一个区域,一般来说有三个地方可以定义变量:
在函数或一个代码块内部声明的变量,称为局部变量。
在函数参数的定义中声明的变量,称为形式参数。
在所有函数外部声明的变量,称为全局变量。

局部变量
在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。

全局变量
在所有函数外部定义的变量(通常是在程序的头部),称为全局变量。全局变量的值在程序的整个生命周期内都是有效的。
全局变量可以被任何函数访问。也就是说,全局变量一旦声明,在整个程序中都是可用的。

从作用域,内存存储方式,声明期,使用者 去考虑区别

在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。

初始化局部变量和全局变量
当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:
int 0 char '\0' float 0 double 0 pointer NULL

  • 局部变量和全局变量的区别
  1. 局部变量定义在一个函数内部,在函数之外是不可访问的。(定义在一个以{}为限的区域内)
  2. 全局变量定义在所有函数之外,并且在其作用域内的所有函数都可以访问。下面做详细讲解。
  3. 局部变量的生存期就是在定义它所在的{}里,全局变量是程序中定义在所有函数(包括 main 函数)之外的任何变量。
  4. 全局变量的作用域是程序从变量定义到整个程序结束的部分。这意味着全局变量可以被所有定义在全局变量之后的函数访问。
#include<iostream>
using namespace std;
void California (); // Function prototype

int BIRDS = 500; // Global constant
int main()
{
    cout <<  "In main there are " << BIRDS << " birds.\n";
    California();
    cout <<  "In main there are " << BIRDS << " birds.\n";
    return 0;
}

void California()
{
    const int BIRDS = 10000;
    cout << "In California there are " << BIRDS << " birds.\n";
}

输出:
In main there are 500 birds.
In California there are 10000 birds.
In main there are 500 birds.

由上面我们暂且去理解一下局部变量和全局变量。

  • C++中声明和定义的区别

《C++Primer》第四版 2.3.5节中这么说到:

  1. 变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。但可以有多个声明
  2. 变量声明:用于向程序表明变量的类型和名字。
  3. 定义也是声明:当定义变量时我们声明了它的类型和名字。
  4. extern关键字:通过使用extern关键字声明变量名而不定义它。
extern int i;// 是声明,不是定义,也即不分配空间。
int i;// 是声明,同时也定义,为其分配了空间。
任何包含了显式初始化的声明即成为定义。
我们能给由extern关键字标记的变量赋一个初始值,但是这么做也就抵消了extern的作用。extern语句如果包含初始值就不再是声明,而变成定义了:
extern int i = 10;//定义,extern作用被抵消

函数的声明和定义区分比较简单,带有{}的即为定义,没带{}即为声明。
int max(int a, int b); //函数的声明

举个例子说明一下定义只有一个,但声明可以有多个

#include<iostream>
using namespace std;
extern int a;//声明
int main(){
	int a = 10;//定义
	int a = 11;//此时,编译器会报错。
	a = 11;//此时,并不会报错,这不在是定义了,而是赋值。
	cout << a << endl;
	return 0;
}
  1. 隐藏。(static函数,static变量均可)
    当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。
    举例来说明。同时编译两个源文件,一个是a.c,另一个是main.c。
//a.c
char a = 'A'; // global variable
void msg()
{
     printf("Hello\n");
}
 
//main.c
 
int main()
{
     extern char a; // extern variable must be declared before use
     printf("%c ", a);
     (void)msg();
     return 0;
}
输出:
Hello

为什么在 a.c 中定义的全局变量a和函数msg能在main.c中使用?
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件 main.c 是可见的。
如果加了static,就会对其它源文件隐藏。例如在amsg的定义前加上static,main.c 就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏。

  1. static的第二个作用是保持变量内容的持久。(static变量中的记忆功能和全局生存期)

存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见
PS:如果作为static局部变量在函数内定义,它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。

#include <stdio.h>
 
int fun(){
    static int count = 10; //在第一次进入这个函数的时候,变量a被初始化为10!并接着自减1,以后每次进入该函数,a
    return count--; //就不会被再次初始化了,仅进行自减1的操作;在static发明前,要达到同样的功能,则只能使用全局变量:    
 
}
 
int count = 1;
 
int main(void)
{
     printf("global\t\tlocal static\n");
     for(; count <= 10; ++count)
               printf("%d\t\t%d\n", count, fun());
     return 0;
}
输出:
global  local static
1 10
2 9
3 8
4 7
5 6
6 5
7 4
8 3
9 2
10 1

– 基于以上两点可以得出一个结论:把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。因此static这个说明符在不同的地方所起的作用是不同的。

  1. static的第三个作用是默认初始化为0(static变量)

其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是·0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘\0’;不妨做个小实验验证一下。

#include <stdio.h>
 
int a;
 
int main()
{
     int i;
     static char str[10];
     printf("integer: %d; string: (begin)%s(end)", a, str);
     return 0;
}
输出:
integer: 0; string: (begin) (end) 

最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0.

在 C 中 s t a t i c 用 来 修 饰 局 部 静 态 变 量 和 外 部 静 态 变 量 、 函 数 。 而 C + + 中 除 了 上
述 功 能 外 , 还 用 来 定 义 类 的 成 员 变 量 和 函 数 。 即 静 态 成 员 和 静 态 成 员 函 数 。

  1. static 在类中的作用

待续----
待参考,尚未看- static在类中的作用
待参考,尚未看- static与const的区别

  • extern 作用总结

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。当用extern声明一个全局变量的时候,首先应明确一点:extern的作用范围是整个工程,也就是说当我们在.h文件中写了extern int a;链接的时候链接器会去其他的 .cpp文件 中找有没有int a的定义,如果没有,链接报错;

  1. extern修饰变量的声明

如果文件 a.c 需要引用 b.c 中变量 int v,就可以在 a.c 中声明extern int v,然后就可以引用变量v
这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说 a.c 要引用到 v,不只是取决于在 a.c 中声明 extern int v,还取决于变量v本身是能够被引用到的。
这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。
还有很重要的一点是,extern int v可以放在 a.c 中的任何地方,比如你可以在a.c中的函数 fun 定义的开头处声明extern int v,然后就可以引用到变量 v 了,只不过这样只能在函数fun作用域中引用 v 罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。

shengming.h

extern int a;
extern int b;

shengming.cpp //此处并不需要加 #include ”shengming.h"

int a = 10;
int b = 20;

main.cpp

#include <iostream>
#include "shengming.h"
using namespace std;
int main()
{
    cout << a << " " << b << endl;
    return 0;
}
输出: 10 20
  1. extern修饰函数声明

从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。
如果文件a.c需要引用 b.c 中的函数,比如在 b.c 中原型是 int fun(int mu),那么就可以在 a.c 中声明 extern int fun(int mu),然后就能使用 fun来做任何事情。
就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在 a.c 的文件作用域的范围中。
对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用 extern 和包含头文件来引用函数有什么区别呢?
extern 的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。
这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。

  1. extern修饰符可用于指示 C或者C++ 函数的调用规范

比如在 C++ 中调用 C 库函数,就需要在 C++ 程序中用 extern “C”声明要引用的函数。

这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是 C++ 和C程序编译完成后在目标代码中命名规则不同。
参考文献 – 使用 extern 混合编译 C 与 C++

  • extern和static修饰全局变量的区别

static关键字的作用有很多,声明静态全局变量,静态局部变量,类的静态成员等。这里主要讨论他在修饰全局变量时与extern的区别。有两点需要注意:
1、static修饰全局变量时,声明和定义是同时给出的;
2、static的全局作用域只是自身编译单元。extern 是整个工程,一个编译单元指一个.cpp文件和他所包含的头文件。也就是说 static 在修饰全局变量时,“全局”的范围是小于 extern的。因此,在头文件中用static声明(和定义)全局变量,是不会出现上面讨论的重复定义的问题的。
参考extern与static修饰全局变量的区别

如果 extern 想访问 其他文件被修饰成 static 的变量,是不被允许的。extern 不是定义,是引入(声明)在其它源文件中定义的非 static 全局变量;

未看参考文献

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值