这里对static
关键字做一下总结,可以回顾一下前面两篇博客的文章。
《C和指针》笔记11: external和internal链接属性
《C和指针》笔记12: 存储类型(自动变量、静态变量和寄存器变量)
- 当它用于函数定义时,或用于代码块之外的变量声明时,
static
关键字用于修改标识符的链接属性,从external
改为internal
,但标识符的存储类型和作用域不受影响。
用这种方式声明的函数或变量只能在声明它们的源文件中访问。
- 当它用于代码块内部的变量声明时,
static
关键字用于修改变量的存储类型(从自动变量修改为静态变量),但变量的链接属性和作用域不受影响。
用这种方式声明的变量在程序执行之前创建,并在程序的整个执行期间一直存在,而不是每次在代码块开始执行时创建,在代码块执行完毕后销毁。
对于1这里举个例子:
C可以用于设计和实现抽象数据类型 (ADT,abstract data type),因为它可以限制函数和数据定义的作用域。这个技巧也被称为黑盒 (blackbox)设计。抽象数据类型的基本想法是很简单的——模块具有功能说明和接口说明,前者说明模块所执行的任务,后者定义模块的使用。但是,模块的用户并不需要知道模块实现的任何细节,而且除了那些定义好的接口之外,用户不能以任何方式访问模块。
限制对模块的访问是通过static关键字的合理使用实现的,它可以限制对那些并非接口的函数和数据的访问。例如,考虑一个用于维护一个地址/电话号码列表的模块。模块必须提供函数,根据一个指定的名字查找地址和电话号码。但是,列表存储的方式是依赖于具体实现的,所以这个信息为模块所私有,客户并不知情。
下一个例子程序说明了这个模块的一种可能的实现方法。头文件
addrlist.h
提供一些由客户使用的接口。程序addrlist.c
展示了这个模块的实现。
addrlist.h
/* ** 地址列表模块的声明。 */ /* ** 数据特征 ** ** 各种数据的最大长度(包括结尾的NUL字节)和地址的最大数量。 */ #define NAME_LENGTH 30 /*允许出现的最长名字 */ #define ADDR_LENGTH 100 /* 允许出现的最长地址 */ #define PHONE_LENGTH 11 /* 允许出现的最长电话号码 */ #define MAX_ADDRESSES 1000 /* 允许出现的最多地址个数 */ /* ** 接口函数 ** ** 给出一个名字,查找对应的地址。 */ char const * lookup_address( char const *name ); /* ** 给出一个名字,查找对应的电话号码。 */ char const * lookup_phone( char const *name );
addrlist.c
/* ** 用于维护一个地址列表的抽象数据类型。 */ #include "addrlist.h" #include <stdio.h> /* ** 每个地址的三个部分,分别保存于三个数组的对应元素中。 */ static char name[MAX_ADDRESSES][NAME_LENGTH]; static char address[MAX_ADDRESSES][ADDR_LENGTH]; static char phone[MAX_ADDRESSES][PHONE_LENGTH]; /* ** 这个函数在数组中查找一个名字并返回查找到的位置的下标。 ** 如果这个名字在数组中并不存在,函数返回-1。 */ static int find_entry( char const *name_to_find ) { int entry; for( entry = 0; entry < MAX_ADDRESSES; entry += 1 ) if( strcmp( name_to_find, name[ entry ] ) == 0 ) return entry; return -1; } /* ** 给定一个名字,查找并返回对应的地址。 ** 如果名字没有找到,函数返回一个NULL指针。 */ char const * lookup_address( char const *name ) { int entry; entry = find_entry( name ); if( entry == -1 ) return NULL; else return address[ entry ]; } /* ** 给定一个名字,查找并返回对应的电话号码。 ** 如果名字没有找到,函数返回一个NULL指针。 */ char const * lookup_phone( char const *name ) { int entry; entry = find_entry( name ); if( entry == -1 ) return NULL; else return phone[ entry ]; }
- 上面的程序是一个黑盒的好例子。黑盒的功能通过规定的接口访问,在这个例子里,接口是函数
lookup_address
和lookup_phone
。但是,用户不能直接访问和模块实现有关的数据,如数组或辅助函数find_entry
,因为这些内容被声明为static
。- 这种类型的实现威力在于它使程序的各个部分相互之间更加独立。例如,随着地址列表的记录条数越来越多,简单的线性查找可能太慢,或者用于存储记录的表可能装满。此时你可以重新编写查找函数,使它更富效率,可能是通过使用某种形式的散列表查找来实现。或者,你甚至可以放弃使用数组,转而为这些记录动态分配内存空间。但是,如果用户程序可以直接访问存储记录的表,表的组织形式如果进行了修改,就有可能导致用户程序失败。
- 黑盒的概念使实现细节与外界隔绝,这就消除了用户试图直接访问这些实现细节的诱惑。这样,访问模块唯一可能的方法就是通过模块所定义的接口。
参考
- 《C和指针》