static(static在c语言中代表什么)新鲜出炉

Mark wiens

发布时间:2024-01-07

C语言程序设计现代方法—第十九章程序设计

static(static在c语言中代表什么)新鲜出炉

 

只要有模块化就有可能发生误解隐藏信息的另一面是阻断沟通00//程序设计编写大型程序的技术展示C语言的特性01//模块模块设计C程序看成独立的模块模块是服务(函数)的集合(被其他部分称为客户)模块都有接口

(头文件)模块的实现是包含(模块中函数的定义源文件)头文件包含函数原型

(eg)程序由calc.c文件和一个栈模块组成由calc.c 文件包含main函数栈模块存储在在stack.h和stack.c 文件中文件calc.c是栈模块的客户(函数的调用)文件stack.h是栈模块的接口

(函数的头文件)stack.c 文件是栈模块的实现(函数定义、变量sheng)C库本身就是一些模块的集合库中每个头都是包含一个模块的接口 是包含输入/输出函数的模块的接口

则是包含字符串处理函数的模块的接口模块的优点抽象可复用性可维护性内聚性与耦合性高内聚性模块中的元素应该彼此紧密相关易使用、易理解低耦合性模块之间应该尽可能相互独立易修改、易复用模块的类型数据池(通常为头文件)

变量或常量的集合不建议变量放在头文件建议常量放在头文件(eg)库库是一个相关函数的集合(eg) 就是字符串处理函数库的接口抽象对象抽象对象是指对于隐藏的数据结构

进行操作的函数的集合-----------------------------------------------------------------------本章中术语“对象”的含义与其他章不同在C语言术

语中对象仅仅是可以存储值的一块内存而在本章中对象是一组数据以及针对这些数据的操作的集合如果数据是隐藏起来的那么这个对象是“抽象的”-----------------------------------------------------------------------

抽象数据类型(ADT)将具体数据实现方式隐藏起来的数据类型称为抽象数据类型客户模块可以使用该类型来声明变量但不会知道这些变量的具体数据结构如果客户模块需要对这种变量进行操作则必须调用抽象数据类型模块所提供的函数

02//信息隐藏信息隐藏设计良好的模块会对它的客户隐藏一些信息我们的栈模块的客户不需要知道栈是用数组、链表还是其他形式存储这种故意对客户隐藏信息的方法称为信息隐藏优点安全性灵活性C语言中强制信息隐藏的主要工具

static存储类型将具有文件作用域的变量声明成static可以使其具有内部链接从而避免它被其他文件(包括模块的客户)访问将函数声明成static也可函数只能被同一文件中的其他函数直接调用栈模块栈模块的两种实

现一种使用数组一种使用链表(eg)模块头文件stack.h#ifndef STACK_H#define STACK_H#include /* C99 only */void make_empty(void);

bool is_empty(void);bool is_full(void);void push(int i);int pop(void);#endif数组实现这个栈stack1.c#include

#include #include "stack.h"#define STACK_SIZE 100static int contents[STACK_SIZE];static int top = 0;

static void terminate(const char *message){ printf("%s\n", message); exit(EXIT_FAILURE);}void make_empty(void)

{ top = 0;}bool is_empty(void){ return top == 0;}bool is_full(void){ return top == STACK_SIZE;}void push(int i)

{ if (is_full()) terminate("Error in push: stack is full.");   contents[top++] = i;}int pop(void)

{ if (is_empty()) terminate("Error in pop: stack is empty."); return contents[--top];}组成栈的变量(contents 和top )

都被声明为static没有理由让程序的其他部分直接访问它们terminate函数也声明为static这个函数不属于模块的接口相反它只能在模块的实现内使用出于编写风格使用宏来指明哪些函数和变量“公有的”(可以在程序的任何地方访问)

“私有的”(只能在一个文件内访问)#define PUBLIC /* empty */#define PRIVATE static将static写成PRIVATE是因为其在C语言用法很多这样子写指明强制信息隐藏

stack1.c可改PRIVATE int contents[STACK_SIZE];PRIVATE int top = 0;PRIVATE void terminate(const char *message) { ... }

PUBLIC void make_empty(void) { ... }PUBLIC bool is_empty(void) { ... }PUBLIC bool is_full(void) { ... }

PUBLIC void push(int i) { ... }PUBLIC int pop(void) { ... }链表实现stack2.c#include #include

#include "stack.h"struct node { int data; struct node *next;};static struct node *top = NULL;static void terminate(const char *message)

{ printf("%s\n", message); exit(EXIT_FAILURE);}void make_empty(void){ while (!is_empty()) pop();}

bool is _empty(void){ return top == NULL;}bool is_full(void){ return false;}void push(int i){ struct node *new_node = malloc(sizeof(struct node));

if (new_node == NULL) terminate("Error in push: stack is full."); new_node->data = i; new_node->next = top;

top = new_node;}int pop(void){ struct node *old_top; int i; if (is_empty()) terminate("Error in pop: stack is empty.");

old_top = top; i = top->data; top = top->next; free(old_top); return i;}is_full函数每次被调用都返回false链表对大小没有限

制所以栈永远不会满程序运行时仍然可能(可能性不大)出现内存不够的问题从而导致push函数失败但事先很难测试这种情况stack1.cstack2.c其实一样的都可匹配模块接口定义03//抽象数据类型抽象数据类型

作为抽象对象的模块(像上一节中的栈模块)有一个严重的缺点无法拥有该对象的多个实例(本例中指多个栈)为了达到这个目的需要进一步创建一个新的类型定义了Stack类型就可以有任意个栈了(eg)两个栈Stack s1, s2;

make_empty(&s1);make_empty(&s2);push(&s1, 1);push(&s2, 2);if (!is_empty(&sl)) printf("%d\n", pop(&s1)); /* prints "1" */

s1 和s2 究竟是什么(结构?指针?)但这并不重要对于栈模块的客户s1 和s2 是抽象 它只响应特定的操作make_empty、is_emptyis_full、push、pop将stack.h改成提供Stack类型的方式

Stack是结构要给每个函数增加一个Stack类型(或Stack *)的形式参数stack.h#define STACK_SIZE 100typedefstruct {int contents[STACK_SIZE];

int top;}Stack;voidmake_empty(Stack *s);boolis_empty(const Stack *s);boolis_full(const Stack *s);void

push(Stack *s, int i);intpop(Stack *s);封装上面的Stack不是抽象数据类型因为stack.h暴露了Stack类型的具体实现方式因此无法阻止客户将Stack变量作为结构直接使用

Stacks1;s1.top =0;s1.contents[top++] = 1;由于提供了对top和contents成员的访问模块的客户可以破坏栈不完整类型C语言提供的唯一封装工具为不完整类型描述了对象但缺少定义对象大小所需的信息

(eg)声明structt;/* incomplete declaration of t *///不完整类型告诉编译器t是一个结构标记但并没有描述结构的成员编译器并没有足够的信息去确定该结构的大小意图是

不完整类型将会在程序的其他地方将信息补充完整不完整类型的使用受限因为编译器不知道不完整类型的大小所以不能用它来声明变量structts;/*** WRONG ***///不可声明变量但是完全可以定义一个指针类型引用不完整类型。

typedefstructt *T;类型T的变量是指向标记为t的结构的指针现在可以声明类型T的变量将其作为函数的参数进行传递并可以执行其他合法的指针运算不能对这些变量使用->运算符因为编译器对t结构的成员一无所知

04//栈抽象数据类型栈抽象数据类型说明抽象数据类型怎样利用不完整类型进行封装开发一个基于前文栈模块的栈抽象数据类型ADT为栈抽象数据类型定义接口我们需要一个定义栈抽象数据类型的头文件给出代表栈操作的函数的原型

stackADT.hStack类型将作为指针指向stack_type结构该结构存储栈的实际内容结构是一个不完整类型在实现栈的文件中信息将变得完整该结构的成员依赖于栈的实现方法stackADT.h (version 1)

#ifndef STACKADT_H#define STACKADT_H#include /* C99 only */typedef struct stack_type *Stack;

Stack create(void);void destroy(Stack s);void make_empty(Stack s);bool is_empty(Stack s);bool is_full(Stack s);

void push(Stack s, int i);int pop(Stack s);#endif包含头文件stackADT.h 的客户可声明Stack类型的变量这些变量都可以指向stack_type结构

之后客户就可以调用在stackADT.h中声明的函数来对栈变量进行操作但是客户不能访问stack_type结构的成员因为该结构的定义在另一个文件中每一个函数都有一个Stack参数或返回一个Stack值Stack变量现在是指针

指向存放着栈内容的stack_type结构函数create和destroy模块通常不需要这些函数但是抽象数据类型需要create会自动给栈分配内存(包括stack_type 结构需要的内存)同时把栈初始化为“空”状态

destroy将释放栈的动态分配内存下面的客户文件可用于测试栈抽象数据类型它创建了两个栈并对它们执行各种操作stackclient.c#include #include "stackADT.h"

int main(void){ Stack s1, s2; int n; s1 = create(); s2 = create(); push(s1, 1); push(s1, 2); n = pop(s1);

printf("Popped %d from s1\n", n); push(s2, n); n = pop(s1); printf("Popped %d from s1\n",n); push(s2, n);

destroy(s1); while (!is_empty(s2)) printf("Popped %d from s2\n", pop(s2)); push(s2,3); make_empty(s2);

if (is_empty(s2)) printf("s2 is empty\n"); else printf("s2 is not empty\n"); destroy(s2); return 0;

}如果栈抽象数据类型的实现是正确的程序将产生如下输出Popped 2 from s1Popped 1 from s1Popped 1 from s2Popped 2 from s2s2 is empty

用定长数组实现栈抽象数据类型实现栈抽象数据类型有多种方法stackADT.c#include #include #include "stackADT.h"#define STACK_SIZE 100

//定义了stack_type 结构//包含一个定长数组(记录栈中内容)和一个整数(记录栈顶)struct stack_type { int contents[STACK_SIZE]; int top;

};static void terminate (const char *message){ printf("%s\n", message); exit(EXIT_FAILURE);}Stack create(void)

{ Stack s = malloc(sizeof(struct stack_type)); if (s == NULL) terminate("Error in create: stack could not be created.");

s->top = 0; return s;}void destroy(Stack s){ free(s);}void make_empty(Stack s){ s->top = 0;}bool is_empty(Stack s)

{ return s->top == 0;}bool is_full(Stack s){ return s->top == STACK_SIZE;}void push(Stack s, int i)

{ if (is_full(s)) terminate("Error in push: stack is full."); s->contents[s->top++] = i;}int pop(Stack s)

{  if (is_empty(s)) terminate("Error in pop: stack is empty."); return s->contents[--s->top];}用-> 运算符而不是. 运算

访问stack_type结构的contents和top成员参数s是指向stack_type结构的指针而不是结构本身改变栈抽象数据类型中数据项的类型改进栈抽象数据类型栈里的项都是整数太具有局限性可为其他基本类型

float 、double、long...也可是结构、联合或指针使栈抽象数据类型更易于针对不同的数据项类型进行修改增加一行类型定义用类型名Item表示待存储到栈中的数据的类型stackADT.h(version 2)

#ifndef STACKADT_H#define STACKADT_H#include /* C99 only */typedef int Item;typedef struct stack_type *Stack;

Stack create(void);void destroy(Stack s);void make_empty(Stack s);bool is_empty(Stack s);bool is_full(Stack s);

void push(Stack s, Item i);Item pop (Stack s);#endif为了跟stackADT.h匹配stackADT.c文件需做相应的修改stack_type结构将包含一个数组

数组的元素是Item类型而不是int类型struct stack_type { Item contents[STACK_SIZE]; int top;};用动态数组实现栈抽象数据类型栈抽象数据类型

每一个栈的大小上限目前设置为100拥有容量不同的栈两方法实现把栈作为链表来实现没有固定的大小限制将栈中的数据项存放在动态分配的数组将栈中的数据项存放在动态分配的数组关键在于修改stack_type结构使contents成员

为指向数据项所在数组的指针而不是数组本身struct stack_type { Item *contents; int top; int size;};增加新成员size来存储栈的最大容量(contents 指

向的数组的长度)这个成员检查“栈满”的情况create函数有一个参数指定所需的栈的最大容量Stack create(int size);改文件stackADT.hstackADT2.c#include#include

#include "stackADT2.h"struct stack_type { Item *contents; int top; int size;};static void terminate (const char *message)

{ printf("%s\n", message); exit(EXIT_FAILURE);}Stack create(int size){ Stack s = malloc(sizeof(struct stack_type));

if (s == NULL) terminate("Error in create: stack could not be created."); s->contents = malloc(size * sizeof(Item));

  if (s->contents == NULL) { free(s); terminate("Error in create: stack could not be created.");

} s->top = 0; s->size = size; return s;}void destroy(Stack s){ free(s->contents); free(s);}void make_empty(Stack s)

{ s->top = 0;}bool is_empty(Stack s){ return s->top == 0;}bool is_full (Stack s){ return s->top == s->size;

}void push(Stack s, Item i){ if (is_full(s)) terminate("Error in push: stack is full."); s->contents[s->top++] = i;

}Item pop(Stack s){ if (is_empty(s)) terminate("Error in pop: stack is empty."); return s->contents[--s->top];

}现在create函数调用malloc两次一次是为stack_type结构分配空间另一次是为包含栈数据项的数组分配空间任意一处malloc失败都会导致调用terminate函数destroy函数必须调用free函数两次

来释放由create分配的内存测试栈抽象数据类型s1 = create();s2 = create();改s1 = create(100);s2 = create(200);用链表实现栈抽象数据类型动态分配数组实现栈抽象数据类型

比使用定长数组更灵活但客户在创建栈时仍然需要指定其最大容量链表来实现栈不需预先设定栈的大小了链表中的结点用如下结构表示struct node { Item data; struct node *next;

};data成员的类型现在是Item而不是intstack_type结构包含一个指向链表首结点的指针struct stack_type { struct node *top;};stackADT3.c

#include#include#include"stackADT.h"structnode { Item data;structnode *next;};struct

stack_type {structnode *top;};staticvoidterminate(constchar *message){printf("%s\n", message);exit(EXIT_FAILURE);

}Stack create(void){ Stack s = malloc(sizeof(struct stack_type));if (s == NULL) terminate("Error in create: stack could not be created."

); s->top = NULL;return s;}voiddestroy(Stack s){ make_empty(s);free(s);}voidmake_empty(Stack s){while

(!is_empty(s)) pop(s);}boolis_empty(Stack s){return s->top == NULL;}boolis_full(Stack s){returnfalse

;}voidpush(Stack s, Item i){structnode *new_node = malloc(sizeof(structnode));if (new_node == NULL) terminate(

"Error in push: stack is full."); new_node->data = i; new_node->next = s->top; s->top = new_node;

}Item pop(Stack s){structnode *old_top; Item i;if (is_empty(s)) terminate("Error in pop: stack is empty."

); old_top = s->top; i = old_top->data; s->top = old_top->next;free(old_top);return i;}05//抽象数据类型的设计问题

抽象数据类型的设计问题命名惯例一个程序中有多个抽象数据类型两个模块中可能具有同名函数会出现名字冲突每个抽象数据类型都需要自己的create函数使用stack_create代替create错误处理栈抽象数据类型通过显示出错消息

或终止程序的方式来处理错误通用抽象数据类型...新语言中的抽象数据类型...

免责声明:本站所有信息均搜集自互联网,并不代表本站观点,本站不对其真实合法性负责。如有信息侵犯了您的权益,请告知,本站将立刻处理。联系QQ:1640731186