Chinaunix首页 | 论坛 | 博客
  • 博客访问: 981738
  • 博文数量: 186
  • 博客积分: 10020
  • 博客等级: 上将
  • 技术积分: 1676
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-14 17:08
文章存档

2011年(5)

2009年(11)

2008年(2)

2007年(111)

2006年(57)

我的朋友

分类: LINUX

2007-03-29 03:25:19

一.结构的基本知识
struct声明定义了一种数据类型
struct point{
     int x;
     int y;
};
struct {...} x,y,z;  <===> int x,y,z;
                     等价
1.这两个声明都将x,y与z声明为指定类型的变量,并且为它们分配存储空间;如果结构声明的后面不带变量表,则不需要为它分配存储空间,它仅仅描述了一个结构的模板或轮廓。
2.在表达式中,可以通过以下形式访问:   结构名.成员
  struct point pt;
  struct point maxpt = { 320, 200 }; 
  printf(" %d, %d", pt.x, pt.y);
 
3.struct rect{
       struct point pt1;
       struct point pt2;
  };
  struct rect screen;
  screen.pt1.x;
 
二.结构与函数
1.结构的合法操作只有几中:
     a.作为一个整体复制和赋值(包括向函数传递参数以及从函数返回值)
     b.通过&运算符取地址
     c.访问其成员
 
2.结构之间不可以进行比较,可以用一个常量成员值列表初始化结构,自动结构也可以通过赋值进行初始化。
 
3传递结构至少有3种方法:
     a.分别传递各个结构成员;
     b.传递整个结构;
     c.传递指向结构的指针;
例:返回一个point类型的结构:
struct point makepoint(int x, int y)
{
     struct point temp;
     temp.x = x;
     temp.y = y;
     return temp;
}
 
4.struct rect screen;
  struct point middle;
  struct point makepoint(int, int);
 
  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 );
/* addpoint函数:将两个点相加 */
   struct point addpoint(struct point p1, struct point p2)
   {
          p1.x += p2.y;
          p1.y += p2.y;
          return p1;
   }
这里函数的参数和返回值都是结构类型,结果赋值给p1(也可以使用显式的临时变量存储)---结构类型的参数和其他类型的参数一样,都是通过值传递的
 
5.传递指针比传递整个结构效率要高。如果传递给函数的结构很大,使用指针方式的效率通常比复制整个结构效率要高。
  结构指针类似于普通变量指针,声明:
  struct point *pp;
  pp被定义为一个指向struct point类型对象的指针,如果pp指向一个point结构,那么*pp即为该结构,而(*pp).x和(*pp).y则是结构成员
例:struct point origin, *pp;
    pp = &origin;
    printf("origin is (%d, %d)\n", (*pp).x, (*pp).y);
   
    也可以:p->结构成员(p是一个指向结构的指针)
    printf("origin is (%d, %d)\n", pp->x, pp->y);
 
6.所有运算符中,下面4个运算符优先级最高:结构运算符"."和"->",用于函数调用的"()"以及用下标的"[]"
  struct {
       int len;
       char *str;
  } *p;
 
  ++p->len:取len的值,然后增加1
  (++p)->len:将先执行p的加1操作,再对len执行操作;
  (p++)->len:将先对len执行操作,然后再对p增加1;
 
  *p->str:读取的是指针str所指向的对象的值;
  *p->str++:先读取指针str指向的对象的值,然后再将str加1(与*s++)相同;<===> (*p->str)++
  *p++->str:先读取指针str指向的对象的值,然后再将p加1;
 
三.结构数组  
1.多个变量对共同构成一个数组;声明了一个结构类型key,并定义了该类型的结构数组keytab,同时分配存储空间。
  数组keytab的每个元素都是一个结构
struct key{
     char *word;
     int count;
}keytab[NKEYS];
又等价于---- struct key{
                 char *word;
                 int  count;
             };
             struct key keytab[NKEYS];
          
2.因为结构keytab包含一个固定的名字集合,所以最好将它声明为外部变量,这样,只需要初始化一次,所有的地方都可以使用。 
struct key {
     char *word;
     int count;
}keytab[]={              
     "auto", 0,
     "break", 0,
     "const", 0
};
 
3.如何使用:
main()
{
   int n;
   char word[MAXWORD];
   if( (n = binsearch(word, keytab, NKEYS)) >= 0)
        keytab[n].count++;
}
int binsearch(char *word, struct key tab[], int n)
{
     int cond , mid;
     if((cond = strcmp(word, tab[mid].word)) < 0)
     return mid;
}
int getword(char *word, int lim)
{
  char *w = word;
}
 
四.指向结构的指针
struct key *binsearch(char *, struct key *, int);
main()
{
  char word[MAXWORD];
  struct key *p;
  if((p = binsearch(word, keytab, NKEYS)) != NULL)
     p->count++;
  for(p = keytab; p < keytab + NKEYS; p++)
    if(p->count > 0)
       printf("hehe jesse");
}
struct key *binsearch(char *word, struct key *tab, int n)
{
   struct key *mid;
    return mid;  或 return NULL;
}
1.首先,binsearch函数在声明中必须表明:它返回的值类型是一个指向struct key类型的指针,而非整型
 
2.其次,keytab的元素在这里是通过指针访问的
 
3.两个指针之间的加法运算是非法的。但是,指针的减法运算却是合法的
 
4.对算法的最重要修改在于,要确保不会生成非法的指针,或者是试图访问数组范围之外的元素。&tab[-1]和&tab[n]都超过了数组tab的范围。
前者是绝对非法的,而对后者的间接引用也是非法的。但是,C语言的定义保证数组末尾之后的第一个元素(即&tab[n])的指针算术运算可以正确执行。
 
5.在这里p是指向结构的指针,则对p的算术运算需要考虑结构的长度,所以p++执行时,将在p的基础上加上一个正确的值,以确保得到结构数组的下一个元素
  for( p = keytab; p < keytab + NKEYS; p++ )
  注:但不要认为结构的长度等于各成员长度的和(因为不同的对象有不同的对齐要求),可用sizeof运算符返回正确的对象长度。
 
五.自引用结构
struct tnode{
    char *word;
    int count;
    struct tnode *left;
    struct tnode *righ;
};
void treeprint(struct tnode *p)
{
     if(p != NULL){
        treeprint(p->left);
        printf("%4d %s\n", p->count, p->word);
        treeprint(p->right);
     }
}
1.关于存储分配问题,假如用一个分配程序来处理多种类型的请求:
  如果指向char类型的指针和指向struct tnode类型的指针,则会出现两个问题:
    a.它如何在大多数实际机器上满足各种类型对象的对齐要求(例如,整型通常必须分配在偶数地址上)?
    b.使用什么样的声明能处理分配程序必须能返回不同类型的指针的问题?
解决方法:
    a.对齐只需要确保分配程序始终返回满足所有对齐限制要求的指针就可以了,其代价是牺牲一些存储空(*注意:这里的malloc函数满足这种分配对齐的要求*)
    b.将malloc等分配函数声明为一个指向void类型的指针,然后再显式地将指针强制转换为所需类型。
    例如: struct tnode *talloc(void)
           {
                return (struct tnode *) malloc(sizeof(struct tnode));
           }
 
2.给大家看一下strdup()函数的实现:
  char *strdup(char *s)
  {
      char *p;
     
      p = (char *) malloc(strlen(s)+1);
      if (p != NULL)
          strcpy(p, s);
      return p;
  }
 
六.类型定义
1.C语言提供一个称为typedef的功能,用来建立新的数据类型名,
  例如:声明typedef int Length;
            typedef char *String;
  那么:String p == char *p
  typedef在语法上类似于存储类extern,static等
 
2.从任何意义上讲,typedef声明并没有创建一个新类型,她只是为某个已存在的类型增加了一个新的名称而已
  实际上,typedef类似于#define语句,但由于typedef是由编译器解释的,因此它的文本替换功能要超过预处理器的能力。
 
3.除了使得表达式简洁之外,使用typedef还有另外两个重要原因:
     a.它可以使程序参数化,以提高程序的可移植性(如果typedef声明的数据类型同机器有关,那么移植到其他机器上时,只需改变typedef类型定义就可以了)
     b.typedef第二个作用是为程序提供更好的说明性---String类型显然比一个声明为指向复杂结构的指针更容易让人理解
    
七.联合
1.联合是可以(在不同时刻)保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对齐要求。
2.联合提供了一种方式,以在单块存储区中管理不同类型的数据,而不需要在程序中嵌入任何同机器有关的信息。
struct {
     char *name;
     int flags;
     int utype;
     union {
         int ival;
         float fval;
         char *sval;
     } u;
} symtab[NSYM];
if(utype == INT)
   printf("%d\n",u.ival);
if(utype == FLOAT)
   printf("%f\n",u.fval);
也可以通过以下语句来引用其成员:
 symtab[i].u.val;
也可以通过下列语句引用字符串sval的第一个字符:
 *symtab.u.sval;
 symtab.u.sval[0]
 
3.实际上,联合就是一个结构,它的所有成员相对于基地址的偏移量都为0,此结构空间要大到足够容纳最"宽"的成员,并且,其对齐方式要适合于联合中所有类型的成员。
(对联合允许的操作与对结构允许的操作相同:作为一个整体单元进行赋值,复制,取地址及访问其中一个成员)
 
4.联合只能用第一个成员类型的值进行初始化,因此,上述联合u只能用整数值进行初始化
 
八.位字段
1.在存储空间很宝贵的情况下,有可能需要将多个对象保存在一个机器字中。一种常用的方法是,使用类似于编译器符号表的单个二进制位标志集合。外部强加的数据格式(比如硬件设备接口)也经常需要从字的部分位中读取数据。
例:#define KEYWORD  01
    #define EXTERNAL 02
    #define STATIC   04

    enum {KEYWORD = 01, EXTERNAL = 02, STATIC = 04};
使用方法:
 flags |=  EXTERNAL | STATIC;     (将flags中的EXTERNAL和STATIC位置为1)
 flags &= ~(EXTERNAL | STATIC);   (将flags中的EXTERNAL和STATIC位置为0)
 if((flags & (EXTERNAL | STATIC)) == 0)...   (当EXTERNAL和STATIC两个位都为0时,该表达式的值为真)
 
2.这里定义了一个变量flags,它包含3个一位的字段。冒号后的数字表示字段的宽度(用二进制位数表示)。字段被声明为unsigned int类型,以保证它们是无符号量。
  struct{
     unsigned int is_keyword : 1;
     unsigned int is_extern  : 1;
     unsigned int is_static  : 1;
  }flags;
单个引用时:
  flags.is_extern = flags.is_static = 1;            将is_extern和is_static位置成1
  flags.is_extern = flags.is_static = 0;            将is_extern和is_static位置成0
  if(flags.is_extern == 0 && flags.is_static == 0)  用于对is_extern和is_static位进行测试
 
3.字段的所有属性几乎都同具体的实现有关,字段是否能覆盖字边界由具体的实现定义。字段可以不命名,无名字段(只有一个冒号和宽度)起填充作用。特殊宽度0可以用来强制在下一个字边界上对齐。
 
4.某些机器上字段的分配顺序不同,有的是从左端至右端进行的,而某些机器则相反。依赖于这些因素的程序是不可移植的
 
5.字段可以仅仅声明为int,为了方便移植,需要显式声明该int类型是signed还是unsigned类型。字段不是数组,并且没有地址,因此对它们不能使用&运算符。
阅读(1738) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~