8.1 函数参数的传递方式
8.1.1 运用值传递方式
函数调用时,为形参分配内存单元,并将实参的值复制到形参中;调用结束,形参所占内存单元被释放,实参的内存单元仍保留并维持原值。
- 特点:形参和实参占用不同的内存单元,因此函数对形参的改变不会影响实参。这就是函数参数的值单向传递规则。
8.1.2 运用地址传递方式
在函数调用时,将实参数据的存储地址作为参数传递给形参。
- 特点:形参和实参占用同样的存储单元,因此函数中对形参值的改变也会改变实参的值,即双向数据传递。例如数组名作为形参时,就是将数组的首地址作为参数进行传递。
8.2 全局变量
- 全局变量可加强函数模块间的数据联系,但又使得这些函数的独立性降低。从模块化程序设计的观点来看这是不利的。因此不是非用不可时,一般不要使用全局变量。
- 在同一源文件中,允许局部变量和全局变量同名。在局部变量的作用域内,全局变量将被屏蔽而不起作用。如果要引用全局变量,则必须在变量名前加两个冒号
::
。 如果在定义处之前要引用全局变量,则需要在前面加上
extern
且不可省略,这叫做全局变量说明。全局变量说明可以出现在整个程序任何地方,而且可以出现多次,但不能对其赋初始值。当然,在先声明后使用的情况下,extern
可以省略不写。全局变量在说明时系统不给其分配内存单元。void main() {
extern int x, y; //全局变量说明
x++; y++;
}
extern int x, y; //全局变量说明
void gx() {
x = 135;
}
int x=0, y=0; //全局变量定义
step1为全局变量的说明,step2为全局变量的定义。step1、2中有某一个对变量a赋了初始值,赋值的就是全局变量的定义,没赋值的就是全局变量的说明。另外,step1、2中有且仅有一个能对变量a赋初始值,不能都赋值或都不赋值,否则编译报错。
extern int a; //step1
void func1() {
... //引用变量a
}
extern int a=2; //step2
void func2() {
... //引用变量a
}
8.3 变量的存储类型
静态存储类型:程序运行期间由系统分配固定的内存单元并一直保持不变,直到整个程序结束,内存空间才被释放。如全局变量。
- 动态存储类型:程序运行期间根据需要进行动态分配内存单元,使用完毕立即释放。如函数的形参。
变量说明的完整形式:存储类型说明符 数据类型说明符 变量名1,变量名2,...,变量名n;
例如:
auto char c1, c2; //自动字符变量
register i; //寄存器型变量
static int a, b; //静态整型变量
extern int x, y; //外部整形变量
auto(自动变量)
- 动态存储类型
- 只能在函数内或复合语句中定义。所以属于局部变量。
- 一般未加存储类型说明符的局部变量都是自动变量。也即
auto
一般都是省略的。
extern(外部变量)
- 静态存储类型
- 外部变量和全局变量是同一类变量的两种不同角度的提法。前者从存储方式提出,后者从作用域提出。
static(静态变量)
静态存储类型
静态局部变量
- 局部变量的说明前加上static说明符就构成静态局部变量。
- 在调用函数或执行复合语句之前就已经生成,退出函数或复合语句后仍然存在并保持现有的值,直到程序终止才消失,即其生命周期为整个源程序。
作用域与auto型变量相同。因此退出函数后,尽管变量仍存在,但不能使用它。
void func() {
static int s; //定义静态局部变量s
}
void main() {
int a;
a=s+5; //错误,对s的引用超出了s的作用域
}
若定义时未赋初值,则系统自动赋值0;若定义时赋了初值,则赋初值操作在程序开始执行时就执行了,且仅会执行一次。 ```c void main() { int i; void func(); //函数说明 for (i=0; i<5; i++) { func(); //函数调用 } } void func() { //函数定义 static int j=0; //静态局部变量 ++j; printf(“%d”,j); }
运行结果: 1 2 3 4 5
尽管 `j` 离开函数 `func` 后就无法使用,但如果再次调用 `func` 时,它又可以继续使用。
3. 静态全局变量
1. 全局(外部)变量的说明前加上static说明符就构成静态全局变量。
1. 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中通过外部变量(extern)说明都是有效的。**而静态全局变量限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其他源文件中不能通过外部变量说明来使用它**。
<a name="jXvWg"></a>
### register(寄存器变量)
寄存器变量存放在CPU的寄存器中,使用时无需访问内存,直接从寄存器中读写,可提高效率。对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量。
1. register型变量属于**动态存储类型**,因此只有局部自动变量和形参才能定义为寄存器变量。
1. register修饰符是**过时的**修饰符,因为目前大多数编译器都能自动优化,因此程序员指定的register型变量可能无效。
<a name="uaoED"></a>
## 8.4 函数的作用域
<a name="7hHK7"></a>
### 8.4.1 内部函数
如果在一个源文件中定义的函数只能被本文件中的函数调用,而不能被同一源程序其他文件中的函数调用,这种函数成为内部函数。例如
```c
static int func(int a, int b) {
return (a>b?a:b);
}
内部函数也称静态函数。此处静态static的含义不是指存储类型,而是指作用域仅限于本文件。因此不同的源文件定义同名的静态函数不会引起混淆。
8.4.2 外部函数
如果函数定义时没有说明 extern
或 static
则隐含为 extern
。
//file1.c
extern int func2(); //外部函数说明错误。因为func2是内部(静态)函数
void main() {
func2(); //错误,无法引用
}
int func1(int a) {
return a;
}
//file2.c
extern int func1(int a); //外部函数说明正确
static int func2() { //内部函数定义
int x=0;
int y = fun1(x); //正确,可以引用
return y;
}
8.5 常态变量const
在C语言中,const是一个变量修饰符。被其修饰的变量称为常态变量。常态变量不能在程序中赋值,只能赋初始值。
const修饰符与也可以修饰形参,如果在函数中对常态形参赋值将引起编译错。
void main() {
int a;
const int b=10; //常态变量
a=20; //正确
b=30; //错误
}
void getdata(const int a) {
a=10; //错误
}