第2章 程 序 结 构

来源:互联网 发布:全景天窗的优缺点 知乎 编辑:程序博客网 时间:2024/06/11 00:27

第2章 程 序 结 构

本章给出C程序和程序执行的一个概述,也介绍了理解C程序和组成成分的重要术语和特征。讨论的主题包括:

* 源文件和源程序

* main函数和程序执行

* 命令行参量分析

* 生存期、范围、可见性和连接

* 名称空间

因为本章是概述,讨论的主题仅包括一些引言材料,有关详细解释参见交叉引用的信息。


源文件和源程序

一个源程序可以分成一个或多个“源文件”或“转换单元”。输入到编译器被称为一个“转换单元”。

语法

转换单元:

外部说明

转换单元 外部说明

外部说明:

函数定义

说明

第3章“说明和类型”中的“说明概述”给出了说明非终结符的语法,本卷后面的“预处理器参考”解释了如何处理转换单元。

注意:有关ANSI语法规则的解释,参见本卷后面的附录A“C语言语法总结”的引言。

一个转换单元的组成成分是包括函数定义和标识符说明的外部说明。这些说明和定义可以在源文件、头文件、库和该程序需要的其它文件中,你必须编译每个转换单元并链接产生的目标文件来生成一个程序。

一个C“源程序”是命令、编译指示、说明、定义、语句块和函数组成的,为了使一个Microsoft C程序生效,每一个组成成分必须具有本书中描述的语法,虽然它们可以以任何次序放在程序中(服从本书描绘的规则)。但这些组成成分在一个程序中的位置不影响一个程序中使用的变量和函数(有关更多信息,参见本章后面的“生存期、范围、可见性和连接”) 源文件不需要包含可执行语句,例如,你可能发现这种方式是有用的,在一个源文件中放置变量的定义,然后在另一个源文件说明引用这些变量。这种技术很容易找到和修改定义,同样的理由,常量和宏经常组成称为“包括文件”或“头文件”的分开文件中,需要时在源文件中引用它们,有关宏和包括文件的信息,参见本卷后面的“预处理器参考”。

预处理器的命令

一个“命令”指示C预处理器在编译之前对程序文本执行特定动作。预处理器命令在本卷后面的“预处理器参考”中进行充分的描述,本例使用预处理器命令#define:

#define MAX 100

这个语句告诉编译器在编辑之前将MAX的每次出现均用100替换。C编译器预处理器命令是:

#define #endif #ifdef #line

#elif #error #ifndef #pragma

#else #if #include #undef

编译指示

Microsoft特殊处

一个“编译指示”指示编译器在编译时执行一个特殊动作。编译指示从编译器到编译器时发生改变。例如,你可以使用optimize编译指示设置你的程序要执行的优化。Microsoft C编译指示有:

alloc_text data_seg inline_recursion setlocale

auto_inline functionintr insic warning

check_stack hdrstop message

code_seg include_alias ptimize

comment inline_depth pack

有关Microsoft C编译器编译指示的描述参见本卷后面“预处理器参考”中的第2章“编译指示命令”。

Microsoft特殊处结束

说明和定义一个“说明”在一个特定变量、函数或类型及其属性之间建立关联。第3章“说明和类型”中的“说明概述”给出了说明非终结符的ANSI语法,一个说明也指出访问一个标识符的地方和时间(标识符的“连接”)。

有关连接的信息参见本章后面的“生存期、范围、可见性和连接”。一个变量的“定义”建立与说明相同的关联,它还导致分配该变量的存储。

例如,main、find和count函数以及var和val变量都依次定义在一个源文件中:

void main()
{
}
int var=0;
double val[MAXVAL];
char find(fileptr)
{
}
int count(double f)
{
}

变量var和val用于find和count函数中,不需要进一步的说明,但它们的名字在main中是不可见的(不能访问)。

函数说明和定义

函数原型建立该函数的名称、其返回值、它的形参的类型和个数。一个函数定义包括函数体。

函数和变量说明可以出现在一个函数定义的内部或外部。一个函数定义中的任何说明称为“内部的”或“局部的”。所有函数定义外部的说明称为“外部的”、“全局的”或“文件范围”。变量定义像说明一样,可以在内部层(一个函数定义内部)或外部层(所有函数的外部)中进行。函数定义总是出现在外部层。函数定义的进一步讨论在第6章“函数”的“函数定义”中。函数原型在第6章的“函数原型”中讨论。

用花括号({})括起来的说明、定义和语句序列称为一个“块”。C中有两种类型的块。复合语句是一个或多个语句组成的语句(参见第5章“语句”中的“复合语句”),它是一种类型的块。另一种类型的块是“函数定义”,它由一个复合语句(函数体)加上该函数关联的“头”(该函数名称、返回类型和形参)。一个块中的另一个块称之为“嵌套”。

注意,所有复合语句都是用花括号括起来的,但并不是花括号中的任何东西都组成一个复合语句,例如,数组、结构或枚举元素的规格都出现在花括号中,但它们不是复合语句。

例子程序

如下C源程序由两个源文件组成,它给出一个C程序中各种可能的说明和定义的概述。本书后面的章节讨论如何编写这些说明、定义和初始化,以及如何使用C关键词如static和extern。printf函数在C头文件STDIO.H中说明。

main和max函数假设在分开的文件中,程序的执行从main函数开始。在main之前不会显式执行用户函数。

/*********************************************************

FILE1.C-mainfunction

**********************************************************/

#define ONE 1
#define TWO 2
#define THREE 3
#include
int a=1; / *定义外部变量的说明 */
int b=2;extern int max(int a,int b); / *函数原型 */
int main() / *main函数的函数定义 */
{
int c; / *两个未初始化的局部变量的定义*/
int d;
extern int u; / *重新引用其它地方定义的外部变量的说明*/
static int v; / *定义具有连续生存期的变量 */
int w=ONE,x=TWO,y=THREE;
int z=0; z=max(x,y); /*可执行的语句*/
w=max(z,w);
printf("%d%d/n",z,w);
return 0;
}

/*********************************************************

FILE2.C - defination of max function

**********************************************************/

int max(int a,int b)            /* 注意形式参数包括在函数头中    */
{
if (a>b)
return(a);
else
return(b);
}

FILE1.C包含max函数的原型,这种说明有时称为“前向说明”,因为在该函数使用之前说明。main函数的定义包括调用max。

以#define开头的行是预处理器命令。这些命令告诉编译器在FILE1.C中分别用1、2和3替换标识符ONE、TWO和THREE。以#include开头的行告诉编译器包括文件STDIO.H,它包含printf函数的原型。预处理器命令在本卷后面的“预处理器参考”中解释。

FILE1.C使用定义说明来初始化全局变量a和b,局部变量c和d说明了但未初始化。所有这些变量都分配了存储。静态和外部变量u和v都自动初始化为0。因此在说明时只有a、b、u和v包含有意义的值,因为它们显式或隐含地初始化。F1LE2.C包含max的函数定义。这个定义满足在FILE1.C中调用max。

标识符的生存期和可见性在本章后面的“生存期、范围、可见性和连接”中讨论。有关函数的更多信息,参见第6章“函数”。


main函数和程序执行

每个C程序有一个必须命名为main的主(main)函数。如果你的代码服从单代码程序设计模式,你可以使用main的宽字符版本wmain。main函数作为程序执行的起始点。通常通过在程序中调用其它函数来控制程序的执行。一个程序执行到main的末尾会终止,但它可以由于各种原因在其它程序的地方终止。这时可能检测到某个错误,你要强制一个程序终止,为此,要使用exit函数。有关使用exit函数的例子和信息参见“MicrosoftVisual C++ 6.0参考库”的“Microsoft Visual C++ 6.0运行库参考”卷。在源程序中的函数实现一个或多个特定功能。main函数调用这些函数实现它们的各自功能。当main调用另一函数时,它把执行控制传给该函数,因此从该函数的第一个语句开始执行。当执行return语句或到达该函数的末尾时,把控制返回给main。

你可以说明任何函数包括main所具有的参数。术语“参数”或“形参”指的是接受一个传给该函数的值的标识符。有关传送参量给参数的信息参见第6章“函数”中的“参数”。当一个函数调用另一个函数时,被调用函数从调用函数获取它的参数值,这些值称为“参量”。你可为main说明形参,以便它使用如下格式从命令行接受参量:

main(int argc,char *argv[],char *envp[])

当你要传送信息给main时,参数习惯上命名为argc和argv,尽管C编译器不要求这些名称。argc和argv由C语言定义。如果传给main第三个参数,习惯上命名这个参数为envp。本章后面的例子说明了如何使用这三个参数访问命令行参量。下面小节解释这些参数。有关main宽字符版本的描述参见下一节“使用wmain”。

使用wmain

Microsoft特殊处

在单代码程序设计模式中,你可以定义main函数的宽字符版本。如果你要编写服从单代码程序设计模式的可移植代码,则使用wmain代替main。

你可以使用类似于main的格式说明wmain的形式参数,然后传送宽字符参量和任选的宽字符环境指针给该程序。wmain的argv和envp参数是wchar_t *类型,例如:

wmain(int argc,wchar_t *argv[],wchar_t *envp[])

如果你的程序使用一个main函数,通过运行库在启动程序时建立多字节字符环境。当需要时(例如调用_wgetenv或_wputenv函数)只建立该环境的一个宽字符拷贝。如果一个MBCS环境已经存在时,第一次调用_wputenv或第一次调用_wgetenv时,则建立对应的宽字符串环境,然后由_wenviron全局变量指向它,该变量是_environ全局变量的宽字符版本。在这时,该环境的两个拷贝(MBCS和单代码)同时存在,并且由操作系统在该程序生存期中进行维护。

类似地,如果你的程序使用一个wmain函数,在程序启动时建立一个宽字符环境并由_wenviron全局变量指向它。在第一次调用_putenv或getenv时建立一个MBCS(ASCII)环境,并由_environ全局变量指向它。有关MBCS环境更多的信息,参见“Microsoft Visual C++参考库”的“MicrosoftVisual C++6.0运行库参考”卷。

Microsoft特殊处结束

参量描述

main和wmain函数的argc参数是一个整数,指出从命令行传给该程序的参量个数,由于程序名称也作为一个参量考虑,argc的值至少为1。argv参数是一个表示程序参量的以空格结尾的字符串的指针数组。该数组的每个元素指向一个传给main(或wmain)的参量的字符串(有关数组的信息,参见第3章“说明和类型”中的“数组说明”)。argv参数可以作为一个类型char的指针数组(char *argv[])或者类型char的指针的指针(char **argv)来说明。对于wmain,argv参数可以作为类型wchar_t的指针数组(wchar_t *argv[])或者类型wchar_t的指针的指针(wchar_t **argv)来说明。第一个字符串(argv[0])是程序名称,最后指针(argv[argc])为NULL(有关获取环境变量信息的另一种方法参见“Microsoft VisualC++6.0运行库参考”中的“getenv” )。

Microsoft特殊处

envp参数是表示用户环境变量中值设置的以空格结尾的字符串的数组的指针。envp参数可以说明为char的一个指针数组(char *envp[])或char的指针的指针(char **envp)。在wmain函数中,envp参数可以说明为wchar_t的指针数组(wchar_t *invp[])或wchar_t的指针的指针(wchar_t **envp)。该数组的末尾由NULL *指针指向。注意,传给main或wmain的环境块是当前环境的一个“冻结的”拷贝。如果你后面经由调用_putenv或_wputenv改变该环境,当前环境(由getenv/_wgetenv和_environ或_wenviron变量返回)将改变,但由envp所指的块不改变。envp参量在C中是ANSI兼容的,但在C++中不是。

Microsoft特殊处结束扩充通配符参量

Microsoft特殊处

当运行一个C程序时,你可以使用两个通配符即问号(?)和星号(*)指出命令行上的文件名和路径参量。

命令行参量由一个称为_setargv(在宽字符环境中称为_wsetargv)的例程处理,它缺省地不扩充argv字符串数组中的通配符为分离的字符串。你可以使用一个功能更强大的版本_setargv通过与setargv.obj文件链接处理通配符来代替正常的_setargv。如果你的程序使用一个wmain函数,则与wsetargv.obj链接。为了与setargv.obj或wsetargv.obj链接,使用/link选项,例如:

cl typeit.c /link setargv.obj

该通配符与操作系统命令中的同样方式进行扩充(如果你不熟悉该通配符,参见你的操作系统用户指南)。在双引号中括起一个参量阻止通配符扩充。在一个括起的参量中,你可以通过在双引号之前加一个反向斜杠(/)来表示双引号文字。如果该通配符参量没有找到匹配的,该参量作为文字传送。

Microsoft特殊处结束


命令行参量的分析

Microsoft特殊处

在解释操作系统命令行给定的参量时,Microsoft C启动代码使用如下规则:

*由空白定界参量,它是一个空格或制表。

*由双引号括起的一个字符串作为单个参量解释,忽略其中包含的空白。一个括起的字符串可以嵌入在一个参量中。注意,插入记号(^)不作为一个转义字符或定界符识别。

*双引号前加一个反向斜杠(/)解释为一个文字双引号(")。

*反向斜杠作为文字解释的,除非它们直接跟一个双引号。

*如果偶数个反向斜杠后跟一个双引号,那么argv数组中的每对反向斜杠(//)用一个反向斜杠代替,且将双引号(")解释为一个字符定界符。

*如果奇数个反向斜杠后跟一个双引号,那么和argv数组中每对反向斜杠(//)用一个反向斜杠代替, 双引号被解释为剩下的一个反向斜杠的转义序列,导致在argv中放置一个文字双引号(")。

如下表说明了传递给argv几个命令行参量例子的解释结果,列出的第2列、第3列和第4列的结果来自后面的ARGS.C程序的输出。

命令行输出argv[1]argv[2]argv[3]"a b c" d ea b cde"ab/"c" "//" dab"c/da///b d"e f"g h a///bdefgha///"b c da/"bcda////"b c" d ea//b cde

* 命令行参量字符串的数组*/ char **envp) /* 环境变量字符串的数组*/

{
int count;
/* 显示每个命令行参量*/
printf("/nCommand-line arguments:/n");
for (count=0;count<argc;count++);
printf("argv[%d] %s/n",count,argv[count]);
/*显示每个环境变量*/ printf("/nEnvironment variables:/n");while (*envp!=NULL)
printf
(" %s/n",*(envp++));
return;
}

这个程序的一个输出例子如下:

Command-line arguments:
argv[0]C:/MSC/TEST.EXE

Environment variables:
COMSPEC=C:/NT/SYSTEM32/CMD.EXE
PATH=c:/nt;c:/binb;c:/binr;c:/nt;c:/system32;c:/word;c:/help;c:/msc;c:/;
PROMPT=[$p]
TEMP=c:/tmp
TMP=c:/tmp
EDITORS=c:/binr
WINDIR=c:/nt

Microsoft特殊处结束

定制命令行处理

如果你的程序不用命令行参量,你可以通过阻止使用实现命令行处理的例程来节省一些空间,这个例程称为_setargv(在宽字符环境中为_wsetargv),正如本章前面“扩充通配符参量”中描述的。为了阻止它的使用,在包含main函数的文件中定义一个不做任何事情的例程,也取名为_setargv(在宽字符环境中为_wsetargv)。那么调用_setargv或_wsetargv满足你定义的_setargv或_westargv而不加载它们的库版本。

类似地,如果你不通过envp参量访问该环境表,可以提供自己的空例程放在环境处理例程_setenvp(或_wsetenvp)中。

如果你的程序调用C运行库中的_spawn或_exec簇的例程,你不要阻止该环境处理例程,因为这个例程用于从繁育进程传递一个环境给新进程。


生存期、范围、可见性和连接

为了理解C程序如何工作的,你必须掌握程序中使用变量和函数的规则。几个概念对于掌握这些规则是重要的:

*生存期

*范围和可见性

*连接

生存期

“生存期”是一个程序执行期间一个变量或函数存在的周期。标识符的存储期间确定了它的生存期。

用存储类指示符static说明的标识具有静态存储期。静态存储期(也称为“全局”)的标识符在一个程序期间具有存储和确定的值。在程序启动之前仅一次初始化就确定了存储和值。以外部或内部连接说明的标识符也具有静态存储期(参见本章后面的“连接”)。

不以static存储类指示符说明的标识符如果在一个函数内部说明,它具有自动存储期,具有自动存储期的标识符(一个局部的标识符)仅在说明或定义该标识符的块中具有存储和确定的值,一个自动标识符在程序每次进入该块时分配新的存储,当程序退出该块时会丢失它的存储(和它的值)。在一个函数中以非连接说明的标识符也具有自动存储期。

如下规则指出一个标识符是否具有全局(静态的)或局部的(自动的)生存期:

*所有函数都具有静态生存期。因此它们存在于程序执行期间的任何时候。在外部层说明的标识符(也就是,程序中函数定义同层的所有块的外部)总是全局(静态)生存期。

*如果一个局部变量有一个初始化器,每次建立该变量时都初始化它(除非它说明成static)。函数参数也有局部生存期,你可以在一个块中通过在说明中包括static存储类指示符来为一个标识符指定全局生存期。一旦说明成static,该变量保留它从该块的一个入口到下一个入口的值。

虽然具有全局生存期的标识符在源程序的整个执行中都存在(例如,一个外部说明的变量或用static关键字说明的局部变量),它可能在程序的所有部分都是不可见的。有关可见性的信息参见“范围和可见性”。有关存储类指示符非终结符的讨论参见第3章“说明和类型”的中“存储类”。如果需要分配存储器(动态的),通过使用特定库例程如malloc来实现。既然动态存储器分配使用库例程,它不作为语言的一部分。参见“Microsoft Visual C++ 6.0运行库参考”中的malloc函数。

范围和可见性

一个标识符的“可见性”确定了可以引用的程序部分即它的“范围”。一个标识符仅在它的“范围”所包括的程序部分是可见的(也就是可以使用的),它对包含它的文件、函数、块或函数原型可以是有限制的(以便增大限制)。一个标识符的范围是可以使用它的名称的程序部分。有时称之为“词法范围”。有四种类型的范围:函数、文件、块和函数原型。除标号外所有标识符都通过其说明所在层来确定它的范围。如下每一种类型的规则确定了一个程序中标识符的可见性:

文件范围

具有文件范围的说明或类型指示符出现在任何块或参数表的外面,可以在说明之后转换单元的任何位置访问。具有文件范围的标识符名称通常称为“全局的”或“外部的”。一个全局标识符的范围开始于它的定义或说明的地方,终止于该转换单位的未尾。

函数范围

一个标号是唯一具有函数范围的标识符。一个标号通过它使用的语句隐含地说明了。在一个函数中标号名称必须是唯一的(有关标号和标号名称的更多信息,参见第5章“语句”中的“goto和标号语句”)。

块范围

具有块范围的标识符的说明或类型指示符出现在一个块内或一个函数定义的形式参数的说明表中。它仅在它说明和定义的地方到包含其定义或说明的块的末尾的范围内是可见的。它的范围限制为该块和该块嵌套调用的块到关闭该关联块的花括号。这样的标识符有时称为“局部变量”。

函数原型范围

具有函数原型范围的标识符的说明或类型指示符出现在一个函数原型中的参数说明表中(不是该函数的说明部分)。它的范围在该函数说明的末尾。

在其它源文件中的变量可见性的适当说明在第3章“说明和类型”中的“存储类”中描述。在外部层用static存储类指示符说明的变量和函数仅在定义它们的源文件中是可见的,所有其它函数是全局可见的。

生存期和可见性总结

表2.1是大多数标识符的生存期和可见性特性的总结。开头三列给出定义生存期和可见性的属性,具有开头三列属性的标识符的生存期和可见性在第4列和第5列中给出。但该表不包括所有可能的情况。有关更多信息参见第3章“说明和类型”中的“存储类”。

表2.1 生存期和可见性总结

属性: 结果:

层 项 存储类指示 符生存期 可见性

文件范围 变量定义 static 全局的 出现它的源文件中的剩余部分

变量说明 extern 全局的 在出现它的源文件中的剩余部分

函数原型或定义 函数原型 static extern 全局的 全局的 单个源文件 源文件的剩余部分

块范围 变量说明 extern 全局的 块

变量定义 static 全局的 块

变量定义 auto或register 局部的 块

如下例子说明了块、嵌套和变量的可见性:

#include int i=1;

/*i定义在外部层 */

Int main() /*main定义在外部层                             */
{
printf("%d/n",i);/*打印1(外部层i的值) */
{ /* 开始第一个嵌套的块 */
int i=2,j=3; /* i和j定义在内部层 */
printf("%d %d/n",i,j); /* 打印2,3 */
{ /* 开始第二个嵌套的块 */
int i=0; /* i重新定义 */
printf("%d %d/n",i,j); /* 打印0,3 */
} /* 结束第二个嵌套的块 */
printf("%d/n",i); /* 打印2(恢复外面的定义) */
} /* 结束第一个嵌套的块 */
printf("%d/n",i); /* 打印1(恢复外层定义) */
return 0;
}

本例中,有四层可见性:外部层和三个块层。在屏幕上打印的值在每行的后面都有注释。

连 接

标识符的名称在不同的范围可以指不同的标识符,一个标识符在不同范围内说明或者在同一范围内说明多次可以通过称为“连接”的过程使之指向相同的标识符或函数。连接确定其中标识符可以引用的程序部分(它的“可见性”)。有三种类型的连接:内部的、外部的和无连接。

内部连接

如果一个对象或一个函数的一个文件范围标识符的说明包含存储类指示符static,该标识符具有内部连接。否则,该标识符有外部连接。有关存储类标识符非终结符的讨论,参见第3章“说明和类型”中的“存储类”。在一个转换单元中,具有内部连接的标识符的每个实例表示同样的标识符或函数。内部连接的标识符对于一个转换单元是唯一的。

外部连接

如果在文件范围层的标识符的第一次说明没有使用static存储类指示符,该对象具有外部连接。

如果一个函数的一个标识符的说明没有存储类指示符。它的连接好象用存储类指示符extern说明来确定。如果一个对象的标识符说明具有文件范围且没有存储类指示符,则它的连接是外部的。

一个具有外部连接的标识符的名称与其它任何相同名称的外部连接的说明表示相同的函数或数据对象,这两个说明可能在同一转换单元中或不同的转换单元中。如果该对象或函数还具有全局生存期,那么该对象或程序由整个程序共享。

无连接的

如果在一个块中的标识符说明没有包括extern存储类指示符,该标识符是无连接的,对于这个函数是唯一的。

如下标识符是无连接的:

*一个标识符说明成非对象或函数的其它任何事情。

*一个标识符说明成一个函数参数。

*一个对象的块范围标识符不以extern存储类指示符说明。

如果一个标识符是无连接的,在同一范围层的相同名称的多次说明(在一个说明或类型指示符中)会产生一个符号重新定义错误。


名称空间

编译器建立“名称空间”用于识别用于各种不同项的标识符。在每个名称空间中的名称必须是唯一的以避免冲突,但相同的标识符可以出现在多个名称空间中。这意味着你可以为两个或多个不同的项使用相同的标识符,提供的项在不同的名称空间中。编译器基于程序中标识符的句法上下文来解决引用。

注意:不要将有限制的C的名称空间表示与C++“名称空间”特征相混淆。有关更多信息参见本卷后面“Microsoft Visual C++ 6.0语言参考”中的第6章“说明”的“名称空间”。

下面列出了C中使用的名称空间的描述:

语句标号

命名的语句标号是语句部分。语句标号的定义总是跟着一个冒号但不是一个case标号部分。语句标号的使用总是跟在goto关键词的后面。语名标号不要与其它名称或其它函数的标号名称进行区分。

结构、联合和枚举标志

这些标志是结构、联合和枚举类型标识符的一部分,如果出现,总是直接跟随保留字struct、union或enum。该标志名称必须与其它相同可见性的结构、枚举或联合标志相区别。

结构或联合的成员

在名称空间中分配与每个结构和联合类型相关联的成员名称,也就是,相同的标识符可以同时是不同结构或联合的成员的组成成分名称。组成成分的定义总是出现在结构或联合类型指示符中。组成成分名称的使用总是直接跟随成员选择运算符(->和.)。在结构或联合中,一个成员名称必须是唯一的,但为了与其它名称区别,要包含不同结构和联合的成员的名称或结构本身的名称。

普通标识符

名称空间中的所有其它名称包括所有变量、函数(包括形式参数和局部变量)和枚举常量。标识符名称具有嵌套的可见性,因此你可以在块中重新定义它们。

typedef标识

符类型定义(typedef)名称不能在同一范围内用作标识符。

例如,因为结构标志、结构成员和变量名称在三种不同的名称空间中,本例中三个项都命名为student不会有冲突 。每个项的上下文允许正确地解释程序中student的每次出现(有关结构的信息,参见第3章“说明和类型”中的“结构说明”):

struct student 
{
char strdent [20]
int class;
int id;
} student;

当student出现在struct关键字之后时,编译器识别它为结构标志。当student出现在一个成员选择地运算符(->或.)之后时,该名称指的是结构成员。在其它上下文中,student指的是结构变量。但为了不造成模糊,建议不重载该标志名称空间。