}
{ }
switch(i_Choice) { }
c_temp=getchar(); i_Choice=atoi(&c_temp); fflush(stdin);
case 1:
fflush(stdin);
printf(\请输入您要删除的学号:\scanf(\head=Del(head,Del_Num); print(head); fflush(stdin); break; fflush(stdin);
printf(\请输入您要插入的学生信息:\scanf(\head=Insert(head,&stu); print(head); fflush(stdin); break; fflush(stdin); Erase(head);
printf(\链表清空成功!\break; fflush(stdin);
printf(\意外的输入!!\
case 2:
case 3:
default:
13. 双向链表
在双链表中,每一个结点都包含两个指针字段,分别用于存放前驱结点地址和后继结点地址。
与单链表相比,双向链表具有更加灵活的优点,从表中的任何一个结点出发,既可以顺着后继指针链往后查找,又可以顺着前驱指针链往前查找。
在双向链表上进行插入和删除操作室,需要同时修改两个方向上的指针。假设采用动态存储的结构,prior表示前驱指针,next表示后驱指针。
1) 插入
在结点p的后面插入一个新的结点s,需要修改4个指针: s->prior-p;
s->next=p->next; p->next->prior=s; p->next=s;
注:这里要注意指针的修改顺序。在修改第二、第三步的指针时,要用到p->next来找到p的后继结点,所以第四步指针的修改要在第二、第三步的指针修改完成后才能进行。 2) 删除
设指针p指向待删除结点,删除操作可通过以下语句完成: (p->prior)->next=p->next; (p->next)->prior=p->prior;
注:这两个语句顺序是可以颠倒的。虽然在执行上述语句后结点p的两个指针域扔指向前驱结点和后继结点,但是在双链表中已经找不到结点p,而且,执行完删除操作以后,还要将结点p所占的存储空间释放。
14. struct,union和预处理命令的概念及使用
1) struct概念及使用
数据经常以成组的形式存在。例如学校必须知道每个学生的基本情况,包括姓名,学号,出生日期等信息。如果这些值能够存储在一起,访问时会简单一些。但是,如果这些值得类型不同,他们就无法存储在同一个数组中。在C中,就会使用结构把不同类型的值存储在一起。
结构是一种聚合数据类型,也是一些值得集合,这些值称为结构的成员,但一个结构的各个成员可能具有不同的类型。
结构定义: struct TypeName { int a; char b; float c; ? };
关键字struct引出了结构的定义。标识符TypeName是结构“标记”,命名了结构的定义,与关键字struct一起使用可声明结构类型的变量,在定义结构体花括号内部声明的变量是结构的成员。同一结构体的成员不能使用相同的名字,但是不同结构可包含同名的成员而不会发生冲突。此外,每个结构定义都要以分号结束。常见的程序设计错误都是忘记结构定义的分号。
编译器不为上述结构定义保留任何内存空间,而是建立了用于声明变量的一种新的数据的类型。声明结构变量与声明其他类型变量类似。
声明语句:
struct TypeName str_Stu,*p_Stu;
把str_Stu声明为struct TypeName类型的变量,p_Stu是指向struct TypeName的指针。
此外,也可以用下述方法声明给定结构类型的变量:在结构定义的花括号之后用逗号隔开变量名列表,再在最后加上结束结构定义的分号。例如:
struct TypeName { int a; char b; float c; ?
} str_Stu,*p_Stu;
如果结构定义中没有包含结构标记名,那么该结构类型只能在定义结构时声明,而不能单独声明。我们在进行代码设计时,最好在建立结构类型时提供结构标记名,以后可以方便地用结构标记名声明该结构类型的新的变量。
当然我们也可以采用typedef来方便的标记结构类型,例如: Typedef struct TypeName { int a; char b; float c; ?
} TypeName;
我们在声明该结构体变量时,就可以直接写为:
TypeName str_Stu,*p_Stu; 结构的初始化
和数组一样,结构也可以用初始化值列表初始化,即在声明结构变量时,在变量名后用等号连接在花括号中的初始化值列表来初始化该结构变量,初始化值用逗号分开。例如:
声明语句:
struct TypeName str_Stu={10,‘a’,1.2};
建立了类型为struct TypeName的变量str_Stu,并将成员a,b,c分别初始化为10,a,1.2。如果初始化的个数少于结构中成员变量的数目,剩余的成员被自动初始化为0。
2) union概念及使用
和结构一样,联合也是一种聚合数据类型,但是其成员共享了同一个存储空间。程序中的变量无非是两种情况:某些变量是相关的,某些是不相关的。联合是用来使相关变量共享存储空间而不是把空间浪费给不使用的变量。联合的成员可以使任何数据类型。用来存储联合的字节至少能够足以存储最大成员。多数情况下,联合包含了两种或多种数据类型。同一时候只能引用一个成员。
联合是用关键字union声明的,其格式与结构的声明是一样的。 声明语句: union Num{
int x; float y; };
表示Num是具有成语int x和float y的union类型。联合通常定义在程序的main函数之前,因而能够被程序中所有函数用来声明变量。
和结构的声明一样,联合的声明仅仅是建立了一种新的数据类型,把联合和结构的声明放在所有函数之外不能建立全局变量。
注意:
在声明语句中,联合只能用与第一个成员具有相同类型的值初始化。例如,声明语句:
union Num value={10};
因为是用int类型的值初始化联合变量value,所以是正确的。下面声明是非法的:
union Num value={1.2};//非法语句 3) 预处理程序宏等的概念及使用
a) 宏的概念及使用
宏是预处理指令#define中定义的一种操纵。和符号常量一样,程序中的宏标识符也在编译之前被文本取代。可以定义带有或不带有参数的宏。预处理就像处理符号常量一样处理不带参数的宏。对带有参数的宏处理方式是:先用替换文本取代参数,然后再把宏展开,即用替换文本取代程序中的标识符和参数列表。例如,定义一个带有求圆面积参数的宏:
#define CIRCLE_AREA(x) (PI*(x)*(x))
不论文件中何时出现CIRCLE_AREA(x),替换文本中的x都会用x的值取代,符号常量PI是定义的符号常量。例如,语句
Area= CIRCLE_AREA(4); 被展开为:
Area= (3.14*(4)*(4))
当然我们也可以把求圆面积写成一个函数,但是函数需要函数调用的开销,而是用宏则可以直接把代码插入到程序中,并且保持了程序的可读性,但是,宏定义也有一个缺点,就是函数会被调用多次。
但是如果这个宏参数是一个函数,那么就有可能被调用多次从而达到不一致的结果,甚至会发生更严重的错误。比如:
#define min(X,Y) ((X) > (Y) ? (Y) : (X)) //...
c = min(a,foo(b));
这时foo()函数就被调用了两次。为了解决这个潜在的问题,我们应当这样写min(X,Y)这个宏:
#define min(X,Y) ({typeof (X) x_ = (X); typeof (Y) y_ = (Y); (x_ < y_) ? x_ : y_; })
({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope)。
b) 条件编译
条件编译能够让程序员控制预处理智力的执行程序和程序代码的编译,每一个条件预处理指令都计算一个整常数表达式的值。不能在预处理指令中计算强制类型转换表达式、sizeof表达式和枚举类型常量。
条件编译的结构与if选择结构非常相似。以以下预处理代码为例: #if !defined(NULL) #defined NULL 0 #endif
这些预处理指令确定是否定义了NULL。如果定义了NULL,表达式defined(NULL)的计算结果为1,否则为0。如果计算结果为0,那么!defined(NULL)结果为1,从而定义NULL为0,否则就跳过#defined指令。每一个#if结构都是用#endif结束的。在使用时,可以把#if defined()和#if !defined()缩写为#ifdef和#ifndef。可以用#elif(等价于 else if)和#else(等价于else)指令测试包含多个部分的条件预处理结构。
c) 特殊符号#、## i. #的使用
在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组
简化理解:#是“字符串化”的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串。
#define ERROR_LOG(module)
fprintf(stderr,\ 转换为:fprintf(stderr,\ERROR_LOG(devied =0);
转换为:fprintf(stderr,\ii. ##的使用
“##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些##来替代空格。
1 #define TYPE1(type,name) type name_##type##_type
2 #define TYPE2(type,name) type name##_##type##_typeTYPE1(int, c); 转换为:int name_int_type ;
(因为##号将后面分为 name_ 、type 、 _type三组,替换后强制连接) TYPE2(int, d);
转换为: int d_int_type ;
(因为##号将后面分为 name、_、type 、_type四组,替换后强制连接) 参考文献:
1. H.M.Deitel P.J.Deitel等《how to program Second Edition》415-420