4.7 作用域

 

4.7 作用域

作用域又称作用范围。在程序中出现的各种标识符,它们的作用域是不同的。不同标识符的作用域是怎样规定的,这就是下边要讨论的内容。

4.7.1 标识符的作用域规则

标识符的作用域规则如下:标识符只能在声明或定义它的范围内是可见的,而在该范围之外是不可见的。

现将其规则说明如下:

  1. 对于大多数标识符,声明和定义是一回事。只有少数标识符例外,声明和定义是两回事,例如外部变量、函数和类等。
  2. 标识符包含常量名、变量名、函数名、类名、对象名、语句标号等。凡是使用标识符规则定义的各种单词都属于标识符。
  3. 作用域有大有小,最大的是整个程序,最小的是块,中间的有文件、类和函数。标识符是在一定范围内定义的。
  4. 可见指的是可以进行存取或访问操作,不可见指的是不可进行存取或访问操作。任何标识符的作用域都遵从上述规则。

4.7.2 作用域的种类

不同标识符定义在不同的范围内,有着不同的作用域。按作用域的大小可分为如下几类:

  • 程序级
  • 文件级
  • 函数级
  • 块级

其中,程序级的作用域最大,它包含着组成该程序的所有文件。属于程序级作用域的有外部函数和外部变量。这类标识符是在某个文件中定义的,在该程序的其他文件中都是可见的,一般在访问之前需要加以声明。

属于文件级作用域的有内部函数和外部静态变量。这种作用域仅在定义它的文件内。对外部静态变量来讲,作用域是从定义时起到该文件结束为止。另外,用宏定义所定义的符号常量一般是属于文件级的。它也是从定义时起,在没有使用#undef命令取消宏定义时,作用域到该文件结束时为止。

属于函数级作用域的有函数的形参和在函数内定义的自动类变量、寄存器类变量和内部静态类变量以及语句标号。这些标识符的作用域都是在它所定义的函数体内,即在定义它的函数体内从定义时开始,到该函数体结束为止。需要指出的是:不包括在该函数体内的分程序或者if语句、switch语句以及循环语句中所定义的变量。

属于块级作用域的有定义在分程序、if语句、switch语句以及循环语句中的自动类变量、寄存器类变量和内部静态类变量。它们也是从定义时开始在其相应的范围内是可见的。

4.7.3 关于重新定义标识符的作用域规定

在存在层次结构的作用域中,变量不能重复定义。这是指在相同的作用域内,不可有同名变量。但是,在不同的作用域内允许对某个变量进行重新定义。例如,在一个函数体内定义了分程序变量a,不能同时又定义一个同名变量a,但是可以在该函数体内的某个分程序中对变量a进行重新定义。下面的程序段是合法的:

void fun() {
    int a;
    {
        float a;
    }
}

这里有两个不同的变量名字都是a。按作用域的规则规定,int型变量a在整个函数fun内都是有效的、可见的。而float型变量a仅在定义它的分程序内是可见的,在该分程序前后的函数体内是不可见的。那么,int型变量a在分程序内(已定义了float型变量a)是否可见呢?这将由重新定义标识符的作用域规定来回答。

重新定义标识符的作用域规定如下:在某个作用范围内定义的标识符在该范围内的子范围内中可以重新定义该标识符。这时原定义的标识符在子范围内是不可见的,但是它还是存在的,只是在子范围内由于出现了同名的标识符而被暂时隐藏起来。过了子范围后,它又是可见的。

在上述例子中,int型变量a在分程序中是不可见的,它被隐藏起来,但它仍然存在。当过了分程序后,它又是可见的。在分程序中,float型变量a是可见的,出了分程序float型变量a是不可见的。

例4.19 分析下列程序的输出结果:

#include <iostream.h>

void main() {
    int a = 5, b = 7, c = 10;
    cout << a << "," << b << "," << c << endl;
    {
        int b = 8;
        float c = 8.8;
        cout << a << "," << b << "," << c << endl;
        a = b;
        {
            int c;
            c = b;
            cout << a << "," << b << "," << c << endl;
        }
        cout << a << "," << b << "," << c << endl;
    }
    cout << a << "," << b << "," << c << endl;
}

执行该程序后,输出结果如下:

5,7,10
5,8,8.8
8,8,8
8,8,8.8
8,7,10

说明:在该程序中,先在函数体内定义了三个int型变量abc;又在外层分程序中重新定义了bc。因此在外层分程序中,a仍然可见,但原来定义的int型的bc被隐藏起来,是不可见的。而重新定义的int型变量bfloat型变量c是可见的。在内层分程序中,又重新定义了int型变量c,它在内层分程序中可见,而外层分程序中的float型变量c被隐藏起来。这里,由于变量a没有被重新定义过,因此它在函数体内的各个分程序内都是可见的。变量a在外层分程序中被改变了值,并保持改变后的值到内层分程序以及后面各个作用域中。

该程序是训练对重新定义标识符作用域规定的理解的很好的例子。请读者通过分析该程序掌握对重新定义标识符作用域的规定,并能在以后的程序设计中熟练运用。

例4.20 分析下列程序的输出结果:

#include <iostream.h>

void main() {
    int x = 3;
    for (; x > 0; x--) {
        int x = 5;
        cout << x << "\t";
    }
    cout << endl << x << endl;
}

执行该程序后,输出结果如下:

5 5 5
0

说明:在该程序中,先在函数体内定义了一个自动变量x,并初始化为3。又在for循环语句的循环体内重新定义了一个int型变量x,并初始化为5。这时,在循环体内,新定义的x是可见的,而原来定义的x被隐藏了起来。只有退出循环后,原来的x才重新可见。因此,该程序的输出结果是for循环中的x值为5,循环结束后的x值为0。

4.7.4 局部变量和全局变量

在C++程序中,所定义的变量可分为局部变量和全局变量两大类。

1. 局部变量

局部变量是指作用域在函数级和块级的变量,包括自动类变量、寄存器类变量和内部静态类变量以及函数参数。自动类变量是在函数体或分程序内定义的变量,它们的作用域分别在所定义的函数体或分程序内。

自动类变量是定义在函数体或分程序内的一种变量,定义时可加auto说明符,也可以省略。与自动类变量作用域相同的另一种变量是寄存器类变量,这种变量也是定义在函数体内或分程序内,定义时前面加说明符register。寄存器类变量有可能被存放到CPU的通用寄存器中。如果被存放到通用寄存器中便可提高存取速度,如果没被存放到通用寄存器中便按自动类变量处理。能否存放到通用寄存器中取决于当时通用寄存器是否空闲。因此,在定义寄存器类变量时,应注意如下几点:

  1. 该变量的数据长度与通用寄存器的长度相当。一般是char型和int型变量。
  2. 寄存器类变量不宜定义过多,因为通用寄存器个数有限。
  3. 要选择一些使用频率高的变量优先定义为寄存器类变量。例如,多重循环的内重循环变量等。

内部静态类变量是定义在函数体内或分程序内,并且用说明符static说明的一种变量。它的作用域与自动变量相同,但它的生存期较长。这是一种可见性与存在性不一致的变量,后面还将详述。

2. 全局变量

全局变量是指作用域在程序级和文件级的变量,包括外部变量和外部静态变量。外部变量的作用域是程序级的,即在一个文件中定义的外部变量,在该程序的其他文件中都可使用。外部变量是一种定义在函数体外,定义时不加任何存储类说明的变量。外部变量在引用之前需要声明,声明外部变量时应在前面加说明符extern表示该变量是外部变量。在一个文件中,先引用后定义的外部变量引用前必须声明,这称为外部变量提前声明。在一个文件中定义的外部变量在另外一个文件要引用,则在引用前必须声明。由此可见,外部变量的定义和声明是两回事。在一个程序中,一个外部变量只能定义一次,但是可以声明多次。

外部静态变量被定义在函数体外,定义时前面加说明符static表示为静态变量。外部静态变量的作用域是从定义该变量时起直到该文件结束,因此,称外部静态变量的作用域为文件级的。外部静态变量和外部变量的寿命都是长的。可见,外部静态变量的可见性与存在性是不一致的,因为它的作用域不是整个程序,但它的寿命存在于整个程序。

外部静态变量定义时有默认值。int型为0,浮点型为0.0,char型为空。

例4.21 分析下列程序的输出结果:

#include <iostream.h>

void other();

void main() {
    int a = 3;
    register int b = 5;
    static int c;
    cout << "a=" << a << ", b=" << b << ", c=" << c << endl;
    other();
    other();
}

void other() {
    int a = 5;
    static int b = 12;
    a += 10;
    b += 20;
    cout << "a=" << a << ", b=" << b << endl;
}

执行该程序,输出结果如下:

a=3, b=5, c=0
a=15, b=32
a=15, b=52

说明:该程序中出现了三种不同存储类的局部变量:自动类、寄存器类和内部静态类。这里,值得注意的在如下两点:

  1. 内部静态类的变量定义时有默认值。int型为0,浮点型的为0.0,char型的为空。外部变量和外部静态变量也是如此。而自动类和寄存器类的变量定义后在赋初值或赋值前没有默认值,其值是无意义的(通称垃圾值)。
  2. 内部静态变量定义在函数体内,它的作用域定义在它的函数体内或分程序内,然而,在定义它的作用域外,它虽然不可见,但是它仍然存在;它没有被释放掉,一旦回到作用域后,仍然保留其原来的值。这便是内部静态类变量的特点,也是它与自动类变量的区别。理解了这一点,就会分析和选用内部静态类变量。

例4.22 分析下列程序的输出结果:

该程序由三个文件组成,编译时要生成一个项目文件,三个文件的内容如下:

文件1(main.cpp)内容如下:

#include <iostream.h>

void fun1(), fun2(), fun3();
int i = 5;

void main() {
    i = 20;
    fun1();
    cout << "main(): i=" << i << endl;
    fun2();
    cout << "main(): i=" << i << endl;
    fun3();
    cout << "main(): i=" << i << endl;
}

文件file1.cpp内容如下:

#include <iostream.h>

static int i;

void fun1() {
    i = 50;
    cout << "fun1(): i (static) = " << i << endl;
}

文件file2.cpp内容如下:

#include <iostream.h>

void fun2() {
    int i = 15;
    cout << "fun2(): i (auto) = " << i << endl;
    if (i) {
        extern int i;
        cout << "fun2(): i (extern) = " << i << endl;
    }
}

extern int i;

void fun3() {
    i = 30;
    cout << "fun3(): i (extern) = " << i << endl;
    {
        int i = 10;
        cout << "fun3(): i (auto) = " << i << endl;
    }
}

执行该程序输出结果如下:

fun1(): i (static) = 50
main(): i = 20
fun2(): i (auto) = 15
fun2(): i (extern) = 20
main(): i = 20
fun3(): i (extern) = 30
fun3(): i (auto) = 10
main(): i = 30

说明:该例的main.cpp文件中首先定义了一个外部变量i,并初始化为5。在main()函数中,将i的值更新为20。在file1.cpp文件中,定义了一个外部静态变量i,在fun1()函数中,将它的值设为50。在file2.cpp文件中,fun2()函数中定义了一个自动变量i,并初始化为15,在if语句中又引用了外部变量i。在fun3()函数中,引用了外部变量i,并将其值设为30,然后在块作用域内重新定义了一个自动变量i,并初始化为10。

不同存储类的变量在不同作用域内的行为是十分重要的。第一个输出结果是调用fun1()函数中的外部静态变量i的值50。第二个输出结果是main()中的外部变量i的值20。第三个输出结果是fun2()函数中的自动变量i的值15。第四个输出结果是fun2()函数中的外部变量i的值20。第五个输出结果是main()中的外部变量i的值20。第六个输出结果是调用fun3()函数中的外部变量i的值30。第七个输出结果是fun3()函数中的自动变量i的值10。最后一个输出结果是main()中的外部变量i的值30。

通过分析不同位置的变量的存储类,可以理解其值的变化。

4.7.5 内部函数和外部函数

根据存储类,函数可分为两类:一类是内部函数,另一类是外部函数。

1. 内部函数

内部函数只在定义它的文件中可以被调用,而在同一程序的其他文件中不可以被调用。定义内部函数的格式如下:

static (类型说明) (函数名) (参数表) {
    (函数体)
}

其中,static是关键字,用它说明的函数是静态函数。

例4.23 内部函数的定义和调用。分析下列程序的输出结果:

#include <iostream.h>

int i = 10;
static int reset(), next(int), last(int), other(int);

void main() {
    int i = reset();
    for (int j = 1; j <= 3; j++) {
        cout << i << "," << j << ",";
        cout << next(i) << ",";
        cout << last(i) << ",";
        cout << other(i + j) << endl;
    }
}

static int reset() {
    return i;
}

static int next(int j) {
    j = i++;
    return j;
}

static int last(int j) {
    static int i = 20;
    j = i--;
    return j;
}

static int other(int i) {
    int j = 15;
    return i + j;
}

该程序执行后的输出结果请读者自己分析。在分析该程序中,请注意:

  1. 该程序中定义了4个静态(即内部)函数,注意定义和说明的方法。
  2. 注意程序中变量ij在各函数中的存储类,其描述如表4-1所示。
变量函数存储类
imain()自动类
ireset()外部类
inext()外部类
ilast()内部静态类
iother()局部变量
jmain()自动类
jnext()局部变量
jlast()局部变量
jother()局部变量

2. 外部函数

外部函数是一种作用域在整个程序中的函数,包含组成该程序的所有文件。外部函数的定义格式如下:

[extern] (类型说明) (函数名) (参数表) {
    (函数体)
}

其中,extern是关键字,它是外部类函数的说明符。一般情况下,在定义函数时可以省略。

例4.24 外部函数的定义、声明和调用。

该程序由三个文件组成,即f1.cppf2.cppf3.cpp

文件f1.cpp内容如下:

#include <iostream.h>

int i = 1;
extern int reset(), next(), last(), other(int);

void main() {
    int i = reset();
    for (int j = 1; j <= 3; j++) {
        cout << i << "," << j << ",";
        cout << next() << ",";
        cout << last() << ",";
        cout << other(i + j) << endl;
    }
}

文件f2.cpp内容如下:

#include <iostream.h>

static int i = 10;

extern int next() {
    return i += 1;
}

extern int last() {
    return i -= 1;
}

extern int other(int i) {
    static int j = 5;
    return i + j;
}

文件f3.cpp内容如下:

extern int i;

extern int reset() {
    return i;
}

该程序由三个文件和五个函数组成。除main()函数外,其余四个函数被定义成外部函数。

该程序与例4.23有相似之处,但是仔细分析却不一样。分析该程序的输出结果时,仍然要搞清楚变量ij在不同的函数中的存储类,否则无法确定它的值。该程序的输出结果,请读者自己分析。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 30
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com
PowerISO 4.7 是一款兼容 Windows 操作系统的光盘映像文件处理软件。它具有多种功能和特点,使它成为用户首选的软件。首先,PowerISO 4.7 可以创建、编辑、提取和转换各种光盘映像文件,如 ISO、BIN、NRG 等。这意味着用户可以方便地从光盘上提取数据、处理光盘映像文件中的信息,而无需将光盘插入到计算机上。 此外,PowerISO 4.7 还提供了虚拟光驱的功能。用户可以创建多个虚拟光驱,并将光盘映像文件加载到这些虚拟光驱中。这减少了使用实体光驱的需求,节省了计算机上的空间,并提高了光盘映像文件的访问速度。 除此之外,PowerISO 4.7 还具有刻录功能。用户可以使用此软件将光盘映像文件刻录到 CD、DVD 或蓝光光盘上。经过良好的刻录质量保证,刻录完成的光盘可以在各种光驱设备上正常播放和使用。 另一个重要的功能是 PowerISO 4.7 可以创建可启动的 USB 光驱。用户可以使用此功能将操作系统的安装文件写入至 USB 设备上,并从 USB 设备上启动计算机进行安装,这对于没有光驱的计算机非常有用。 总的来说,PowerISO 4.7 是一款功能全面、易于使用且稳定的软件。它的光盘映像文件处理和虚拟光驱功能使用户可以方便地操作光盘映像文件,而刻录和创建可启动 USB 光驱的功能则进一步提升了用户的使用体验。无论是个人用户还是商业用户,都可以从 PowerISO 4.7 中受益。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值