作者shyang55 ()
看板C_and_CPP
标题[语法] 作用范围(scope) 与 生命期(lifetime)
时间Thu Apr 12 06:03:33 2007
最近想把程式写的有系统一点, 就研究了一下变数储存等级(storage class)
想说把心得整理成笔记, 顺便分享给大家~ 下面讨论的主要以 C语言 为主
1. 区块 (block)
例如: { .... }
区块就是任何以大括号包起来的区域, 如函式区块, if区块, for区块
严格说起来, 在任何 statement 出现之前, 一定要把 block 内使用到的变数定义好
在 block 外定义的变数就是 global variable
在 block 内定义的变数就是 local variable
在 block 内想要使用不在 scope 里的 global variable, 就要用 extern 宣告此变数
在 block 外想要使用 local variable, 很可惜, C语言没有支援这种功能
在 block 内定义的变数, 会遮蔽 block 外定义的变数
2. 宣告 (declaration)
例如: (auto) int a; // in block, 定义 + 宣告
int a; // out block, 定义 + 宣告
extern int a; // out block, 宣告
宣告主要是告诉 compiler 说 "这个变数之前有定义过了, 我现在要用这个变数罗"
如果配合储存等级, 就是说 "此变数的 scope 和 lifetime 是如 keyword 所指定的"
如果变数使用前没有先宣告, 会有 undeclare 的错误
我觉得宣告最主要的作用就是把变数的 scope 和 lifetime 定出来
3. 定义 (definition)
例如: (auto) int a; // in block, 定义 + 宣告
int a; // out block, 定义 + 宣告
extern int a = 0; // out block, 定义 + 宣告 (其实 extern 是多余的)
定义就是 memory or stack 配一个空间给变数, 一个变数当然只能有一个空间
local auto variable 是在 run-time 的时候由 stack 配给空间, 初值是随机给的
其他的情况, 则是在程式开始执行的时候就占有 memory 的空间, 初值是0
local static variable 如 { static int a = 5; } 只会在第一次定义时给初值
之後再碰到此叙述时, 会将其视为宣告, 而不会再做初始化的动作
定义有宣告的效果, 但是宣告没有定义的效果
在同一个 scope 里面, 定义只能有一次, 宣告则可以有很多次
例外: int a;
{
int a;
}
虽然第一个 int a; 的 scope 有触及到 block 里面
但是第二个 int a; 把第一个 int a; 的效果遮蔽掉了, 所以不能算定义两次
如果变数在同一个 scope 里定义两次以上, 会有 redefine 的错误
如果变数只有宣告而没有定义, 会有 undefine 的错误
4. 作用范围 (scope)
作用范围就是一个变数能触及到的视野, 在变数的 scope 里, 使用该变数都有效
每种 storage class 都有其自然视野(natural scope)
想要扩展变数的 scope, 就要使用 extern 宣告来增加其腹地
自然视野大致可分为 block scope, file scope 两种
block scope 就是从变数宣告开始, 到 block 结束为止
file scope 就是从变数宣告开始, 到 file 结束为止
会发生 undeclare 的错误, 就是因为没在某变数的 scope 里使用该变数
5. 生命期 (lifetime)
生命期就是一个变数生存的时间, 当变数的 lifetime 过了
则这个变数就完全无效了, memory 里的空间也就 free 掉了, 即使再宣告也没用
会发生 undefine 的错误, 就是因为没在某变数的 lifetime 里使用该变数
会发生 redefine 的错误, 就是因为两个同名的变数, 在同一时期生活在一起
这边同样要注意 block 的遮蔽效果, 可以让两个同名的变数生活在不同的世界
变数的生命期分两种
一种是 local auto variable, 其生命期是从 stack 配空间开始, 到 block 结束为止
其他的情况, 都是从程式执行开始, 到程式结束为止
6. 储存等级 (storage class)
储存等级的 keyword: auto, static, extern, register
这边不讨论 register, 因为现在的 compiler 有时候比人还强, 比人更了解底层机器
而 extern 主要只是搭配 global variable 去拓展 scope 而已
剩下的两个 keyword (auto, static), 再配上 block 的观念, 只剩四种可能
| | |
in/out block | keyword | natural scope | lifetime
| | |
========================================================================
| | |
| (auto) int a; | block scope | to block end
| | |
local ----------------------------------------------------------
| | |
| static int a; | block scope | whole program
| | |
------------------------------------------------------------------------
| | |
| int a; | file scope | whole program
| | |
global ----------------------------------------------------------
| | |
| static int a; | file scope | whole program
| | (strictly) |
------------------------------------------------------------------------
※ static 在 local variable 的作用是改变 lifetime
※ static 在 global variable 的作用是影响 scope 扩展的限制
如果 global variable 没有加 static 修饰, 则可以用 extern 跨 file 宣告
如果 global variable 有使用 static 修饰, 则不能用 extern 跨 file 宣告
所以利用 static 可以做到变数封装的效果, 也就是 C++ class 的效果
※ extern 只对 global variable 有作用而已
※ 若在 block 内使用 extern, 则所增加的 scope 为 block scope
若在 block 外使用 extern, 则所增加的 scope 为 file scope
7. 函式 (function)
函式也是一个外在个体, 可以完全视为一个 global variable
以上所有适用於 global variable 的条例, 都适用於 function
8. 动态记忆体配置 (dynamic memory allocation)
动态记忆体配置一般是由 malloc 以及 free 这一对 C library function call 完成
最大的好处在於可以自由的指定配置时机, 以及自由的指定配置空间
动态记忆体配置只是一个经由 system call 向 OS 要记忆体的方式
所以它没有宣告和定义的行为, 当然也没有 lifetime 和 scope 的观念
因此配置的空间从 malloc 开始, 到 free 掉为止, 都存在着
只要有指标指向这个空间, 就可以去存取, 一次存取的大小以指标的 data type 决定
而释放记忆体空间时, 只要有指标指向这段空间的开头, 再用 free 即可
9. 编译 (compile)
编译的时候, 是一次 compile 一个 file, 就算再怎麽大的程式
compiler 一次也只看一个档案, 顶多先把一些 global variable 定义记起来
等到 link 阶段的时候, 看看会不会有 redefine or undefine 的情况
所以要写一个大程式, 只要注意几件事
(1) 整个 program 里面, 每个 global variable 都有定义, 而且只有定义一次
(2) 每个 file 里面, 任何一个变数在使用前, 都有先宣告过
(3) 使用 local auto variable 时, 要注意其 lifetime
(4) 使用 extern 时, 该变数在别的地方要有定义
(5) 在 block 内可以利用 static 来增加 lifetime
(6) 在 block 外可以利用 static 来封装资料
(7) 要注意 block 的遮蔽效果
(8) 在 .h 档里面, 要使用 ifndef, define 等条件式编译, 避免 redefine 的情况
大概就是这些重点罗, 如果有什麽地方的观念有错, 欢迎大家指正, 谢谢~
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 140.112.29.206
1F:推 Skymaker:推,整理的不错 04/12 09:31
2F:→ neutronstars:推~ 04/12 10:30
3F:推 final01:那static function呢?有人写我无法体会用意~ 04/12 15:20
4F:推 wlsabcd:static function只能在同一个file里的function被呼叫 04/12 21:14
5F:推 asglay:有关scope的部分现在似乎都交给namespace去做而少用static 04/13 04:31
6F:推 ginobilidola:推 有很多很有用的观念~ 04/13 17:08
※ 编辑: shyang55 来自: 140.112.29.206 (04/16 03:04)
7F:推 wixter:推 借转XD 10/20 21:28
8F:推 uj2003:好文借转 04/12 09:49