结构是一个名字或多个变量的集合,这些变量可能为不同类型,为了处理的方便而将这些变量组织在一个名字之下.
6.2 结构与函数
结构的合法操作只有几种:作为一个整体复制和赋值,通过&运算符取地址,访问其成员.其中,复制和赋值包括向函数传递参数以及从函数返回值.结构之间不可以进行比较.可以用一个常量成员值初始化结构,自动结构也可以通过赋值进行初始化.
我们先声明一个点和一个通过两个点确定的矩形:
struct point{
int x;
int y;
};
struct rect{
struct point pt1;
struct point pt2;
};
我们声明一个makepoint函数,用来生成一个点:
struct point makepoint( int x, int y )
{
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}
则现在我们就可以对point进行随意的操作了:
struct rect screen;
struct point middle;
screen.pt1 = makepoint( 0, 0 );
screen.pt2 = makepoint( XMAX, YMAX );
middle = makepoint( ( screen.pt1.x + screen.pt2.x ) / 2, ( screen.pt1.y + screen.pt2.y ) / 2 );
我们也可以将两个点进行相加:
struct point addpoint( struct point p1, struct point p2 )
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
这里p1一定要返回的:还记得C语言里面的参数都是传值的吗?
如果传递给函数的结构很大,使用指针方式的效率通常比复制整个结构的效率要高.结构指针类似于普通变量指针.声明:
struct point *pp;
将pp定义为一个指向struct point类型对象的指针.如果pp指向一个point结构,那么*pp即为该结构,而(*pp).x和(*pp).y则是结构成员.
6.3 结构数组
我们来编写一个程序,用来统计输入中C语言关键字出现的次数:
习题6-1:关于预处理器控制指令,字符串常量,我都不懂什么意思其实,然后就按照自己的想法,把注释添加上了上去进行排除:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
struct key{
char *word;
int count;
} keytab[] = {
"auto", 0,
"break", 0,
"case", 0,
"char", 0,
"const", 0,
"continue", 0,
"default", 0,
"unsigned", 0,
"void", 0,
"volatile", 0,
"while", 0
};
#define NKEYS ( sizeof( keytab ) / sizeof( keytab[ 0 ] ) )
#define MAXWORD 100
int getword( char *, int );
int binsearch( char *, struct key *, int );
int main(void)
{
int n;
char word[ MAXWORD ];
while ( getword( word, MAXWORD ) != EOF ){
if ( isalpha( word[ 0 ] ) ){
if ( ( n = binsearch( word, keytab, NKEYS ) ) >= 0 ){
keytab[ n ].count++;
}
}
}
for ( n = 0; n < NKEYS; n++ ){
if ( keytab[ n ].count > 0 ){
printf("%4d %s\n", keytab[ n ].count, keytab[ n ].word );
}
}
return 0;
}
int binsearch( char *word, struct key *tab, int n )
{
int cond;
int low, high, mid;
low = 0;
high = n - 1;
while ( low <= high ){
mid = ( low + high ) / 2;
if ( ( cond = strcmp( word, tab[ mid ].word ) ) < 0 ){
high = mid - 1;
}
else if ( cond > 0 ){
low = mid + 1;
}
else{
return mid;
}
}
return -1;
}
int getword( char *word, int lim )
{
int ch;
int isBreak = 0;
int isDoubleNote = 0;
while ( --lim && ( ch = getchar() ) != EOF ){
if ( '/' == ch ){
if ( '*' == ( ch = getchar() ) ){
isBreak = 1;
continue;
}
else if ( '/' == ( ch = getchar() ) ){
isDoubleNote = 1;
continue;
}
else{
*word++ = '/';
continue;
}
}
else if ( ( '*' == ch ) && isBreak ){
if ( '/' == ( ch = getchar() ) ){
isBreak = 0;
continue;
}
}
else if ( '\n' == ch && isDoubleNote ){
isDoubleNote = 0;
continue;
}
else if ( ( isalnum( ch ) || '_' == ch ) && ( 1 != isBreak ) && ( 1 != isDoubleNote ) ){
*word++ = ch;
}
else{
break;
}
}
*word = '\0';
return ch;
}
程序输出:
不过我看了一下习题的答案,发现自己肤浅了...对C语言的认知不够深入,所以看待一个问题也很肤浅,希望自己能多努力一下,加深对C语言的了解.下列代码是习题答案书上给的答案:
int getword( char *word, int lim )
{
int c, d, comment( void ), getch( void );
void ungetch( int );
char *w = word;
while ( isspace( c = getch() ) ){
;
}
if ( c != EOF ){
*w++ = c;
}
if ( isalpha( c ) || c == '_' || c == '#' ){
for ( ; --lim > 0; w++ ){
if ( !isalnum( *w = getch() ) && *w != '_' ){
ungetch( *w );
break;
}
}
}
else if ( c == '\'' || c == '"'){
for ( ; --lim > 0; w++ ){
if ( ( *w = getch() ) == '\\' ){ //转义字符
*++w = getch();
}
else if ( *w == c ){
w++;
break;
}
else if ( *w == EOF ){
break;
}
}
}
else if ( c == '/' ){
if ( ( d = getch() ) == '*' ){
c = comment();
}
else{
ungetch( d );
}
}
*w = '\0';
return c;
}
int comment( void )
{
int c;
while ( ( c = getch() ) != EOF ){
if ( c == '*' ){
if ( ( c = getch() ) == '/' ){
break;
}
else{
ungetch( c );
}
}
}
return c;
}
6.4 指向结构的指针
为了进一步说明指向结构的指针和结构数组,我们重新编写关键字统计程序,采用指针.
main和binsearch函数必须修改:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD 100
int getword( char *, int );
struct key *binsearch( char *, struct key *, int );
int main(void)
{
char word[ MAXWORD ];
struct key *p;
while ( getword( word, MAXWORD ) != EOF ){
if ( isalpha( word[ 0 ] ) ){
if ( ( p = binsearch( word, keytab, NKEYS ) ) != NULL ){
p->count++;
}
}
}
for ( p = keytab; p < keytab + NKEYS; p++ ){
if ( p->count > 0 ){
printf("%4d %s\n", p->count, p->word );
}
}
return 0;
}
struct key *binsearch( char *word, struct key *tab, int n )
{
int cond;
struct key *low = &tab[ 0 ];
struct key *high = &tab[ n ];
struct key *mid;
while ( low < high ){
mid = low + ( high - low ) / 2;
if ( ( cond = strcmp( word, mid->word ) ) < 0 ){
high = mid;
}
else if ( cond > 0 ){
low = mid + 1;
}
else{
return mid;
}
}
return NULL;
}
6.5 自引用结构
考虑一个很简单的用二叉树插入单词的程序:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD 100
struct tnode{
char *word;
int count;
struct tnode *left;
struct tnode *right;
};
struct tnode *addtree( struct tnode *, char * );
void treeprint( struct tnode * );
int getword( char *, int );
int main( void )
{
struct tnode *root;
char word[ MAXWORD ];
root = NULL;
while ( getword( word, MAXWORD ) != EOF ){
if ( isalpha( word[ 0 ] ) ){
root = addtree( root, word );
}
}
treeprint( root );
return 0;
}
struct tnode *talloc( void );
char *strdup( char * );
struct tnode *addtree( struct tnode *p, char *w )
{
int cond;
if ( NULL == p ){
p = talloc();
p->word = strdup( w );
p->count = 1;
p->left = p->right = NULL;
}
else if ( ( cond = strcmp( w, p->word ) ) == 0 ){
p->count++;
}
else if ( cond < 0 ){
p->left = addtree( p->left, w );
}
else{
p->right = addtree( p->right, w );
}
return p;
}
void treeprint( struct tnode *p )
{
if ( NULL != p ){
treeprint( p->left );
printf("%4d %s\n", p->count, p->word );
treeprint( p->right );
}
}
struct tnode *talloc( void )
{
return ( struct tnode * )malloc( sizeof( struct tnode ) );
}
char *strdup( char *s )
{
char *p;
p = ( char * )malloc( strlen( s ) + 1 );
if ( NULL != p ){
strcpy( p, s );
}
return p;
}
几乎所有介绍二叉树的都以类似的例子开头,故不对此例子进行解释.
习题6-2:
用一棵树来存储前六个字母相同的单词,而用一个单链表来存储一棵树.这道题太多坑了.....不过书上提供的例子太经典了...然后就写出来了.....
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE 128
typedef struct TREENODE{
char *data; //出现的单词
int count; //该单词出现的次数
struct TREENODE *left;
struct TREENODE *right;
} tNode;
typedef struct SLIST{
char *tag; //标签--前n个字母是相同的
tNode *data; //前n个相同字母所保存的树
struct SLIST *left;
struct SLIST *right;
} sList;
sList *addList( sList *treeList, char *line ); //将一行的单词增加到列表中
void printNode( tNode *treeNode );
void printList( sList *treeList ); //打印列表
char *getword( char *line, char *word ); //从一行中逐个获取单词
sList *insertList( sList *treeList, char *tag, char *word ); //将单词插入到列表中
tNode *addTree( tNode *treeNode, char *word );
int main( void )
{
sList *treeList = NULL;
char line[ MAXLINE ];
while ( NULL != fgets( line, MAXLINE, stdin ) ){
treeList = addList( treeList, line ); //通过回传链表达到修改链表的目的(PS:伟大的the c programming language,以前学习<C和指针>的时候,只会将地址传递进去,程序变得非常的难看)
}
printList( treeList );
}
sList *addList( sList *treeList, char *line )
{
char temp[ 128 ]; //这里之所以用到字符串数组而不是用到char *,是因为我们需要改变这个字符串
char tag[ 128 ];
memset( temp, 0, 128 );
memset( tag, 0, 128 );
while ( ( *line != '\n' ) && ( line = getword( line, temp ) ) ){ //之所以要加上*line != '\n',是因为一行结尾的时候,line实际上包含两个字符"\n\0".
strncpy( tag, temp, 2 );
treeList = insertList( treeList, tag, temp );
}
return treeList;
}
char *getword( char *line, char *word )
{
while ( !isalpha( *line ) ){
line++;
}
while ( isalpha( *line ) ){
*word++ = *line++;
}
*word = '\0';
return line;
}
sList *insertList( sList *treeList, char *tag, char *word )
{
int cond;
int i = 0;
char *tempWord = ( char * )malloc( sizeof( char ) * 128 ); //这里之所以要用一个malloc的变量来存储word,是因为word本身是一个指针,函数外任何一个地方对word的操作均会影响到这里的值.
char *tempTag = ( char * )malloc( sizeof( char ) * 128 );
memset( tempWord, 0, 128 );
memset( tempTag, 0, 128 );
while ( '\0' != *word ){
tempWord[ i++ ] = *word++;
}
tempWord[ i ] = '\0';
i = 0;
while ( '\0' != *tag ){
tempTag[ i++ ] = *tag++;
}
tempTag[ i ] = '\0';
if ( NULL == treeList )
{
tNode *treeNode = ( tNode * )malloc( sizeof( tNode ) );
treeNode->data = tempWord;
treeNode->count = 1;
treeNode->left = treeNode->right = NULL;
treeList = ( sList * )malloc( sizeof( sList ) );
treeList->tag = tempTag;
treeList->data = treeNode;
treeList->left = treeList->right = NULL;
}
else if ( ( cond = strcmp( tempTag, treeList->tag ) ) == 0 ){
addTree( treeList->data, tempWord );
}
else if ( cond < 0 ){
treeList->left = insertList( treeList->left, tempTag, tempWord );
}
else{
treeList->right = insertList( treeList->right, tempTag, tempWord );
}
return treeList;
}
tNode *addTree( tNode *treeNode, char *word )
{
int cond;
if ( NULL == treeNode ){
treeNode = ( tNode * )malloc( sizeof( tNode ) );
treeNode->data = word;
treeNode->count = 1;
treeNode->left = treeNode->right = NULL;
}
else if ( ( cond = strcmp( word, treeNode->data ) ) == 0 ){
treeNode->count++;
}
else if ( cond < 0 ){
treeNode->left = addTree( treeNode->left, word );
}
else{
treeNode->right = addTree( treeNode->right, word );
}
return treeNode;
}
void printList( sList *treeList )
{
if ( NULL != treeList ){
printList( treeList->left );
printNode( treeList->data );
printList( treeList->right );
}
}
void printNode( tNode *treeNode )
{
if ( NULL != treeNode ){
printNode( treeNode->left );
printf("%s occures %d\n", treeNode->data, treeNode->count );
printNode( treeNode->right );
}
}
程序输出:
习题6-3:
我写的代码有个不好的地方是:对于单链表插入节点,我就简单的把节点插入在头部(这很容易),而不是插入在尾部.这导致一种情况是:输出似乎不那么漂亮:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE 1000
struct WORD;
typedef struct LINELIST{
int lineNum;
struct WORDTREE *word;
struct LINELIST *next;
}lineList;
typedef struct WORDTREE{
char *word;
struct LINELIST *lineNum;
struct WORDTREE *left;
struct WORDTREE *right;
} wordTree;
lineList *insertTreeList( lineList *lList, int lineNumber );
wordTree *insertTree( wordTree *wTree, char *word, int lineNumber );
wordTree *insertListTree( wordTree *wTree, char *word );
lineList *insertList( lineList *lList, char *word, int lineNumber );
char *getword( char *line, char *word );
void printList( lineList *lList );
void printTree( wordTree *wTree );
void printListTree( wordTree *wTree );
void printTreeNode( lineList *lList );
int main( void )
{
char *line = ( char * )malloc( sizeof( char ) * MAXLINE );
char temp[ MAXLINE ];
int lineNumber = 0;
wordTree *wTree = NULL;
lineList *lList = NULL;
memset( line, 0, MAXLINE );
memset( temp, 0, MAXLINE );
while ( NULL != fgets( line, MAXLINE, stdin ) ){
lineNumber++;
while ( ( *line != '\n' ) && ( line = getword( line, temp ) ) ){
int i = 0;
char *tempWord = ( char * )malloc( sizeof( char ) * MAXLINE );
memset( tempWord, 0, MAXLINE );
while ( '\0' != temp[ i ] ){
tempWord[ i ] = temp[ i ];
i++;
}
tempWord[ i ] = '\0';
wTree = insertTree( wTree, tempWord, lineNumber );
lList = insertList( lList, tempWord, lineNumber );
}
}
printTree( wTree );
printf("\n------------\n");
printList( lList );
return 0;
}
char *getword( char *line, char *word )
{
while ( !isalpha( *line ) ){
line++;
}
while ( isalpha( *line ) ){
*word++ = *line++;
}
*word = '\0';
while ( isspace( *line ) && '\n' != *line ){ //省略换行符前面的空格
line++;
}
return line;
}
wordTree *insertTree( wordTree *wTree, char *word, int lineNumber )
{
int cond;
if ( NULL == wTree )
{
wTree = ( wordTree * )malloc( sizeof( wordTree ) );
wTree->word = word;
wTree->left = wTree->right = NULL;
wTree->lineNum = NULL;
wTree->lineNum = insertTreeList( wTree->lineNum, lineNumber );
}
else if ( ( cond = strcmp( word, wTree->word ) ) == 0 ){
wTree->lineNum = insertTreeList( wTree->lineNum, lineNumber );
}
else if ( cond < 0 ){
wTree->left = insertTree( wTree->left, word, lineNumber );
}
else{
wTree->right = insertTree( wTree->right, word, lineNumber );
}
return wTree;
}
lineList *insertTreeList( lineList *lList, int lineNumber )
{
if ( NULL == lList ){
lList = ( lineList * )malloc( sizeof( lineList ) );
lList->lineNum = lineNumber;
lList->next = NULL;
lList->word = NULL;
}
else{
lineList *newNode = ( lineList * )malloc( sizeof( lineList ) );
newNode->lineNum = lineNumber;
newNode->word = NULL;
newNode->next = lList;
lList = newNode;
}
return lList;
}
void printTree( wordTree *wTree )
{
if ( NULL != wTree ){
printTree( wTree->left );
printf("%s : ", wTree->word );
printTreeNode( wTree->lineNum );
printTree( wTree->right );
}
}
void printTreeNode( lineList *lList )
{
while ( NULL != lList ){
printf("%3d ", lList->lineNum );
lList = lList->next;
}
printf("\n");
}
lineList *insertList( lineList *lList, char *word, int lineNumber )
{
if ( NULL == lList ){
lList = ( lineList * )malloc( sizeof( lineList ) );
lList->lineNum = lineNumber;
lList->next = NULL;
lList->word = NULL;
lList->word = insertListTree( lList->word, word );
}
else{
lineList *newnode = ( lineList * )malloc( sizeof( lineList ) );
newnode->lineNum = lineNumber;
newnode->word = NULL;
newnode->word = insertListTree( newnode->word, word );
newnode->next = lList;
lList = newnode;
}
return lList;
}
wordTree *insertListTree( wordTree *wTree, char *word )
{
int cond;
if ( NULL == wTree ){
wTree = ( wordTree * )malloc( sizeof( wordTree ) );
wTree->word = word;
wTree->lineNum = NULL;
wTree->left = wTree->right = NULL;
}
else if ( ( cond = strcmp( word, wTree->word ) ) == 0 ){
return wTree;
}
else if ( cond < 0 ){
wTree->left = insertListTree( wTree->left, word );
}
else{
wTree->right = insertListTree( wTree->right, word );
}
return wTree;
}
void printList( lineList *lList )
{
while ( NULL != lList ){
printf("%d : ", lList->lineNum );
printListTree( lList->word );
printf("\n");
lList = lList->next;
}
}
void printListTree( wordTree *wTree )
{
if ( NULL != wTree ){
printListTree( wTree->left );
printf("%s ", wTree->word );
printListTree( wTree->right );
}
}
程序输出:
习题6-4:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE 128
typedef struct WORDTREE{
char *word;
int count;
struct WORDTREE *left;
struct WORDTREE *right;
}wordTree;
char *getword( char *line, char *word );
wordTree *addTree( wordTree *wTree, char *word );
void printTree( wordTree *wTree );
wordTree *sortTreeNode( wordTree *wTree );
wordTree *insertTree( wordTree *sortTree, wordTree *wTree );
int main( void )
{
wordTree *wTree = NULL;
wordTree *sortTree = NULL; //用于排序
char *line = ( char * )malloc( sizeof( char ) * MAXLINE );
char *word = ( char * )malloc( sizeof( char ) * MAXLINE );
memset( line, 0, MAXLINE );
memset( word, 0, MAXLINE );
while ( NULL != fgets( line, MAXLINE, stdin ) ){
while ( ( *line != '\n' ) && ( line = getword( line, word ) ) ){
int i = 0;
char *tempWord = ( char * )malloc( sizeof( char ) * MAXLINE );
memset( tempWord, 0, MAXLINE );
while ( '\0' != *word ){
tempWord[ i++ ] = *word++;
}
tempWord[ i ] = '\0';
wTree = addTree( wTree, tempWord );
}
}
printTree( wTree );
sortTree = sortTreeNode( wTree );
printf("\n------------\n");
printTree( sortTree );
return 0;
}
char *getword( char *line, char *word )
{
while ( !isalpha( *line ) ){
line++;
}
while ( isalpha( *line ) ){
*word++ = *line++;
}
while ( isspace( *line ) && '\n' != *line ){
line++;
}
return line;
}
wordTree *addTree( wordTree *wTree, char *word )
{
int cond;
if ( NULL == wTree ){
wTree = ( wordTree * )malloc( sizeof( wordTree ) );
wTree->count = 1;
wTree->word = word;
wTree->left = wTree->right = NULL;
}
else if ( ( cond = strcmp( word, wTree->word ) ) == 0 ){
wTree->count++;
}
else if ( cond < 0 ){
wTree->left = addTree( wTree->left, word );
}
else{
wTree->right = addTree( wTree->right, word );
}
return wTree;
}
void printTree( wordTree *wTree )
{
if ( NULL != wTree ){
printTree( wTree->left );
printf("%s : %d\n", wTree->word, wTree->count );
printTree( wTree->right );
}
}
wordTree *sortTreeNode( wordTree *wTree )
{
static wordTree *sortTree = NULL;
if ( NULL != wTree ){
sortTreeNode( wTree->left );
sortTree = insertTree( sortTree, wTree );
sortTreeNode( wTree->right );
}
return sortTree;
}
wordTree *insertTree( wordTree *sortTree, wordTree *wTree )
{
if ( NULL == sortTree ){
sortTree = ( wordTree * )malloc( sizeof( wordTree ) );
sortTree->count = wTree->count;
sortTree->word = wTree->word;
sortTree->left = sortTree->right = NULL;
}
else if ( wTree->count <= sortTree->count ){
sortTree->left = insertTree( sortTree->left, wTree );
}
else{
sortTree->right = insertTree( sortTree->right, wTree );
}
return sortTree;
}
程序输出:
6.6 表查询
一个表查找程序包.假设:
#define IN 1
则当我们遇到:
state = IN;
就必须用1来替换IN.
我们采用散列查找方法---将输入名字转换为一个小的非负整数,该整数随后将作为一个指针数组的下标.数组的每个元素指向某个链表的表头,链表中的各个块用于描述具有该散列值的名字.如果没有名字散列到该值,则数组元素的值为NULL.
链表的定义如下:
struct nlist{ /* 链表项 */
struct nlist *next; /* 链表中下一表项 */
char *name; /* 定义的名字 */
char *defn; /* 替换文本 */
};
相应的指针数组定义如下:
#define HASHSIZE 101
static struct nlist *hashtab[ HASHSIZE ];
一个简短的散列函数:
unsigned hash( char *s )
{
unsigned hashval;
for ( hashval = 0; *s != '\0'; s++ ){
hashval = *s + 31 * hashval;
}
return hashval % HASHSIZE;
}
lookup(s)函数在表中查找s,若找到,则返回指向该处的指针;若没找到,则返回NULL.
struct nlist *lookup( char *s )
{
struct nlist *np;
for ( np = hashtab[ hash( s ) ]; np != NULL; np = np->next ){
if ( strcmp( s, np->name ) == 0 ){
return np;
}
}
return NULL;
}
install函数借助lookup函数判断加入的名字是否已经存在.如果已存在,则用新的定义取而代之;否则,创建一个新表项.如无足够空间创建新表项,则install函数返回NULL.
char *strdup( char * );
struct nlist *install( char *name, char *defn )
{
struct nlist *np;
unsigned hashval;
if ( ( np = lookup( name ) ) == NULL ){
np = ( struct nlist * )malloc( sizeof( *np ) );
if ( np == NULL || ( np->name = strdup( name ) ) == NULL ){
return NULL;
}
hashval = hash( name );
np->next = hashtab[ hashval ];
hashtab[ hashval ] = np;
}
else{
free( ( void * )np->defn );
}
if ( ( np->defn = strdup( defn ) ) == NULL ){
return NULL;
}
return np;
}
习题6-5:
void undef( char *s )
{
int h;
struct nlist *prev, *np;
prev = NULL;
h = hash( s );
for ( np = hashtab[ h ]; np != NULL; np = np->next ){
if ( strcmp( s, np->name ) == 0 ){
break;
}
prev = np;
}
if ( np != NULL ){
if ( prev == NULL ){
hashtab[ h ] = np->next;
}
else{
prev->next = np->next;
}
free( ( void * )np->name );
free( ( void * )np->defn );
free( ( void * )np );
}
}
对于习题6-6,实际上不知道从何下手,于是简单的看看答案,把答案粘贴出来(PS:我表示答案看得也是一知半解)
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD 100
struct nlist{
struct nlist *next;
char *name;
char *defn;
};
void error( int, char * );
int getch( void );
void getdef( void );
int getword( char *, int );
struct nlist *install( char *, char * );
struct nlist *lookup( char * );
void skipblanks( void );
void undef( char * );
void ungetch( int );
void ungets( char * );
int main( void )
{
char w[ MAXWORD ];
struct nlist *p;
while ( getword( w, MAXWORD ) != EOF ){
if ( strcmp( w, "#" ) == 0 ){
getdef();
}
else if ( !isalpha( w[ 0 ] ) ){
printf("%s", w );
}
else if ( ( p = lookup( w ) ) == NULL ){
printf("%s", w );
}
else{
ungets( p->defn );
}
}
return 0;
}
void getdef( void )
{
int c, i;
char def[ MAXWORD ], dir[ MAXWORD ], name[ MAXWORD ];
skipblanks();
if ( !isalpha( getword( dir, MAXWORD ) ) ){
error( dir[ 0 ], "getdef:expecting a directive after #" );
}
else if ( strcmp( dir, "define" ) == 0 ){
skipblanks();
if ( !isalpha( getword( name, MAXWORD ) ) ){
error( dir[ 0 ], "getdef:non-alpha -name expected" );
}
else{
skipblanks();
for ( i = 0; i < MAXWORD - 1; i++ ){
if ( ( def[ i ] = getch() ) == EOF || def[ i ] == '\n' ){
break;
}
}
def[ i ] = '\0';
if ( i <= 0 ){
error( '\n', "getdef: incomplete define" );
}
else{
install( name, def );
}
}
}
else if ( strcmp( dir, "undef" ) == 0 ){
skipblanks();
if ( !isalpha( getword( name, MAXWORD ) ) ){
error( name[ 0 ], "getdef:non-alpha in undef" );
}
else{
undef( name );
}
}
else{
error( dir[ 0 ], "getdef:expecting a directive after #" );
}
}
void error( int c, char *s )
{
printf("error:%s\n", s );
while ( c != EOF && c != '\n' ){
c = getch();
}
}
void skipblanks( void )
{
int c;
while ( ( c = getch() ) == ' ' || c == '\t' ){
;
}
ungetch( c );
}
6.7 类型定义
C语言提供了一个称为typedef的功能,它用来建立新的数据类型名:
typedef struct WORDTREE{
char *word;
int count;
struct WORDTREE *left;
struct WORDTREE *right;
}wordTree;
从任何意义上讲,typedef声明并没有创建一个新类型,它之四海为某个已存在的类型增加了一个新的名称而已.typedef声明也没有增加任何新的语义:通过这种方式声明的变量与通过普通声明方式声明的变量具有完全相同的属性.实际上,typedef类似于#define语句,但由于typedef是由编译器解释的,因此它的文本替换功能要超过预处理器的能力:
typedef int ( *PFI )( char *, char * );
该语句定义了类型PFI是"一个指向函数的指针,该函数具有两个char *类型的参数,返回值为类型int"
typedef有两个重要的原因:首先,它可以使程序参数化,以提高程序的可移植性.其次为程序提供更好的说明性.
6.8 联合
联合是可以(在不同时刻)保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对齐要求.联合提供了一种方式,以在单块存储区中管理不同类型的数据,而不需要在程序中嵌入任何同机器有关的信息.
联合可以使用在结构和数组中,反之亦可.
struct{
char *name;
int flags;
int utype;
union{
int ival;
float fval;
char *sval;
} u;
} symtab[ NSYM];
我们可以如下访问联合的数据:
symtab[ i ].u.ival;
symtab[ i ].u.sval;
symtab[ i ].u.sval[ 0 ];
实际上,联合就是一个结构,它的所有成员相对于基地址的偏移量都为0,此结构空间要大到足够容纳最"宽"的成员,并且,其对齐方式要适合于联合中所有类型的成员.对联合允许的操作与对结构允许的操作相同:作为一个整体单元进行赋值,复制,取地址及访问其中一个成员.
联合只能用其第一个成员类型的值进行初始化,因此,上述联合u只能用整数值进行初始化.
6.9 位字段
当我们进行一个字的位读取的时候,通常会遇见下列的代码:
#define KEYWORD 01
#define EXTERNAL 02
#define STATIC 04
flags |= EXTERNAL | STATIC;
flags &= ~( EXTERNAL | STATIC );
但C语言提供了位字段的能力:
struct {
unsigned int is_keyword : 1;
unsigned int is_extern : 1;
unsigned int is_static : 1;
} flags;
其中,冒号后面的数字代表字段的宽度(用二进制数表示).
则我们可以更自然的修改flags了:
flags.is_extern = flags.is_static = 1;
flags.is_extern = flags.is_static = 0;
字段不是数组,并且没有地址,因此对它们不能使用&运算符.