Warning:
为了避免非零基础人群感到身体不适、头晕恶心、易怒及粗口,请不要查看以下内容。
上节我们重点学习了函数有关知识,本节我们将会了解到头文件与几种变量,它们都是C语言中的核心内容,一定要好好学习,打牢基础。本节内容琐碎繁多,一定要保持清醒的头脑和清晰的逻辑思维。
第4章 函数与程序结构
4.5 头文件
这里简洁的概括几点,并不打算深入研究。
简单一句话:在include的地方,把头文件里的内容原封不动的复制到引用该头文件的地方。
头文件里一般包括宏定义, 全局变量, 函数原型声明,头文件。
头文件引用有两种形式:#include <stdio.h> 和 include "main.h ”。
用< >引用的一般是编译器提供的头文件,编译时会在指定的目录中去查找头文件。具体是哪个目录,编译器知道,我们不用关心。
用“ ”引用的一般是自己写的头文件,编译时,编译器会在项目所在的文件夹中进行查找。
总结:系统提供的头文件用< >引用, 自己写的用“ ”引用。
我们可以把程序源文件分割成几个部分,这样便于程序的维护与阅读。此外,我们还应考虑定义和声明在这些文件之间的共享文件。我们尽可能的把能够共享的部分集中在一起,这样就只需要一个副本,改进程序时也容易保证程序的正确性。我们把这些公共副本放在头文件calc.h中,在需要使用该头文件的源文件中使用#include指令将它们包含进来。
总结:对于某些中等规模的程序,最好只用一个头文件存放程序中各部分共享的对象。较大的程序需要使用更多的头文件,我们需要精心的组织它们。
4.6 静态变量
静态变量是指仅供其所在的源文件中的函数使用,其它函数不能访问的变量。用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。通过static限定外部对象,可以达到隐藏外部对象的目的,
要将对象对象指定为静态存储,可以在正常的对象声明前加上关键字extern作为前缀。
static也可以声明内部变量。static类型内部变量同自动变量一样,是某个特定函数的局部变量,只能在该函数中使用,但它与自动变量不同的是,不管其所在函数是否被调用,它一直存在,而不像自动变量那样,随着函数被调用和退出而存在和消失。换句话说,static类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量。
4.7 寄存器变量
register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将register变量放在机器的寄存器中,这样可以使程序更小、执行速度更快。但编译器可以忽略此选项。
register声明只适用于自动变量以及函数的形式参数。
实际使用时,底层硬件环境的实际情况对寄存器变量的使用会有一些限制。每个函数中只有很少的变量可以保存在寄存器中,且只允许某些类型的变量。但是,过量的寄存器声明并没有什么害处,这是因为编译器可以忽略过量的或不支持的寄存器变量声明。另外,无论寄存器变量实际上是不是存放在寄存器中,它的地址都是不能访问的。在不同的机器中,对寄存器变量的数目和类型的具体限制也是不同的。
4.8 程序块结构
C语言并不是程序块结构的语言,它不允许函数中定义函数。但是,在函数中可以以程序块结构的形式定义变量。变量的声明(包括初始化)除了可以紧跟在函数开始的花括号之后,还可以紧跟在任何其它标识复合语句开始的左花括号之后。以这种方式声明的变量可以隐藏程序块外与之同名的变量,它们之间没有任何关系,并在与左花括号匹配的右花括号之前一直存在。
例如,在下面的程序段中:
if(n > 0){
int i; /* 声明一个新的变量i */
for(i = 0; i < n; i++)
...
}
变量i的作用域是if语句的“真”分支,这个i与该程序块外声明的i无关,每次进入程序块时,在程序内声明以及初始化的自动变量都将被初始化。静态变量只在第一次进入程序块时被初始化一次。
自动变量(包括形式参数)也可以隐藏同名的外部变量与函数。在下面的声明中:
int x;
int y;
f(double x)
{
double y;
...
}
函数f内的变量x引用的是函数的参数,类型为double,而在函数f外,x是int类型的外部变量。这段代码中的y也是如此。
在一个好的程序设计风格中,应该避免出现变量名隐藏外部作用域中相同名字的情况。否则,很可能引起混乱和错误。
4.9 初始化
前面我们多次提到初始化的概念,不过始终没有详细讨论。本节将对前面讨论的各种存储类的初始化规则做一个总结。
在不进行初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义(即初值为无用信息)。
定义标量变量时,可以在变量名后紧跟一个等号和一个表达式来初始化变量:
int x = 1;
char squote = '\'';
long day = 1000L * 60L * 60L * 24L; /* 每天的毫秒数 */
对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次。对于自动变量与寄存器变量,则在每次进入函数或程序块时都将被初始化。
对于自动变量与寄存器变量来说,初始化表达式可以不是常量表达式:表达式中可以包含任意在此表达式之前已经定义的值,包括函数调用。
数组的初始化可以在声明的后面紧跟一个初始化表达式列表,初始化表达式列表用花括号括起来,各初始化表达式之间通过逗号分隔。例如,如果用一年中各月的天数初始化数组days,其变量的定义如下:
int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
当省略数组长度时,编译器将把花括号中初始化表达式的个数作为数组的长度。在本例中长度为12。
如果初始化表达式的个数比数组元素少,则对外部变量、静态变量、自动变量来说,没有初始化表达式的元素将被初始化为0。如果初始化表达式的个数比数组元素数多,则是错误的。不能一次将一个初始化表达式指定给多个数组元素,也不能跳过前面的数组元素而直接初始化后面的数组元素。
字符数组的初始化比较特殊:可以用一个字符串来代替用花括号括起来并用逗号分隔的初始化表达式序列。例如:
char pattern[] = "ould";
它等价于下面的声明:
char pattern[] = { 'o', 'u', 'l', 'd', '\0' };
这种情况下,数组长度为5。
总结
本节我们学习了头文件相关知识,学会了静态变量与寄存器变量,了解了C语言程序块结构相关内容和初始化的相关内容。这些知识如果都能够被熟练运用,一定能够锦上添花,为我们程序设计节省大量时间和精力,下节我们会继续学习本章最后剩余的内容。