10 程序结构

10.1 局部变量

局部:在函数体内声明的变量称为相对于函数的局部。
存储期限(storage duration):调用闭合函数时“自动”分配局部变量的存储单元,函数返回时收回分配。
特点:

  • 自动存储期限:调用闭合函数时“自动”分配局部变量的存储单元,函数返回时收回分配。在闭合函数返回时收回分配。
  • 程序快作用域:变量的作用域是可以参考变量的程序文本的部分。从变量声明的点开始一直到闭合函数的末尾。局部变量拥有程序块作用域。
1
2
3
4
5
6
7
int log2(int n){
int log = 0;/*局部变量*/
while(n > 1){
n /= 2;
log++;
}
}

10.1.1 静态存储期限:static

特点:具有静态存储期限的变量拥有永久的存储单元,所以会在整个程序执行期间会保留变量的值。

  1. 静态局部变量始终有程序块作用域,它对其他函数而言是不可见的
  2. 静态变量是隐藏来自其他函数的数据的地方,但是它会为将来同一个函数的调用保留这些数据。

声明方式:在局部变量声明中放置单词static可以使用变量从自动存储期限变为静态存储期限。

1
2
3
4
5
void f(void){
/*因为局部变量i已经声明为static,所以在程序执行期间它占有同样的存储单元。在f返回时,变量i不会丢失自身的值。*/
static int i;
...
}

10.1.2 形式参数

和局部变量比较

  • 相同点:自动存储期限、块级作用域
  • 不同点:在每次函数调用时对形式参数自动进行初始化(调用中通过赋值获得实际参数的值)

10.2 外部变量

又名:全局变量
声明位置:外部变量是声明在任何函数体外的。
性质:

  • 静态存储期限:同声明的static的局部变量一样,外部变量拥有静态存储期限。存储在外部变量中的值将永久保留下来。
  • 文件作用域:从变量声明的点开始一直到闭合文件的末尾。跟随在外部变量声明后的所有函数都可以访问它。

10.2.1 程序:用外部变量实现栈

栈(stack):像数组一样,栈可以存储具有相同数据类型的多个数据项。
操作方式:LIFO(后进先出),占中数据项的操作是十分受限制的,可以忘栈中压入数据,或者从栈中弹出数据项。禁止测试或修改不在栈顶的数据项。
c语言实现:把元素存储在数组中,称为constents,命名为top的一个整型变量用来标记栈栈顶的位置。栈为空时,top值为0。为了往栈中压入数据项,可以把数据项简单存储在contents中标记为top的位置上,然后自增top。弹出数据项则要求自减top,然后用它作为contents中标记为top的位置上,谈后自增top。弹出数据项则要求自减top,然后用它作为contents的索引取回弹出的数据项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#define STACK_SIZE 100
#define TRUE 1
#define FALSE 0

typedef int Bool;

int contents[STACK_SIZE];
int top = 0;

/**
* 清空栈
* 将top指针指向数组的第一项
*/

void make_empty(void){
top = 0;
}

/**
* 检查栈是否为空
* @return true:空;false:非空
*/

Bool is_empty(void){
return top == 0;
}

/**
* 检查栈是否已满
* @return true:已满;false:未满
*/

Bool is_full(void){
return top == STACK_SIZE;
}

/**
* 向栈中压入数据
* @param i 要压入的数据项
*/

void push(int i){
if(is_full()){
stace_overflow();
}else{
contents[top++] = i;
}
}

/**
* 从栈中弹出数据项
* @return 数据项
*/

int pop(void){
if(is_empty()){
stack_underflow();
}else{
return contents[--top];
}
}

10.2.2 外部变量的利与弊

利:有利于多个函数必须共享一个变量或者上述几个函数共享大量变量。
蔽:

  1. 可维护性差:如果改变外部变量,那么需要检查同一个文件中的每个函数,确认该改变对函数的影响。
  2. 可读性差:如果外部变量出现问题,难以确定导致这个值发生错误的函数。
  3. 可复用性差:很难再其他城程序中复用依赖于外部变量的函数。因为以来外部变量的函数不是“独立的”。

技巧:为了提高可阅读性和可能的错误,使用外部变量时,确保它们都拥有有意义的名字。

10.2.3 程序:猜数(略)

10.3 程序块

语法:

1
2
3
4
{
多条声明
多条语句
}

作用域:程序块中的变量具有进入程序块时为存储变量分配单元,而在退出程序块时解除分配。
函数体(也是程序块)放置临时变量的优点:

  1. 避免函数体起始位置的声明与只是临时使用的变量相混淆
  2. 减少了名字冲突
1
2
3
4
5
6
if(i > j){
int temp;
temp = i;
i = j;
j = temp;
}

10.4 作用域

规则:

  1. 标识符拥有文件作用域,即在所声明的闭合程序块内有效
  2. 新的声明临时会“隐藏”旧的声明
  3. 在程序块结束后后面,被“隐藏”的标识符重新获得旧的含义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*第1个*/
int i;

/*第2个*/
void f(int i){
i = 1;
}

void g(void){
/*第3个*/
int i = 2;
if(i > 0){
/*第4个*/
int i;
i = 3;
}
i = 4;
}

/**/
void h(void){
i = 5;
}

10.5 构建c 程序

程序构成要素:

  • 预处理指令:诸如#include#define
  • 类型定义
  • 函数声明和外部变量声明
  • 函数定义

建议编排顺序:

  1. #include指令
  2. define指令
  3. 类型定义(typedef)
  4. 外部变量声明
  5. 除main函数之外的函数原型
  6. main函数的定义
  7. 其他函数的定义

建议函数注释:盒形注释

  • 函数名
  • 描述函数的目的
  • 讨论每个参数的含义
  • 描述返回值
  • 罗列任何的副作用