Chinaunix首页 | 论坛 | 博客
  • 博客访问: 838579
  • 博文数量: 91
  • 博客积分: 2544
  • 博客等级: 少校
  • 技术积分: 1885
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-12 09:08
文章存档

2016年(10)

2014年(2)

2013年(4)

2012年(23)

2011年(23)

2010年(13)

2009年(14)

2007年(2)

分类: C/C++

2009-07-11 10:38:07

C语言的声明解析

    C语言的声明比较复杂,到现在还有一些声明搞得不是很清楚,正好有点时间,把C的声明复习一下,记一下自己的复习笔记吧。

1, 解读C声明的方法
    在《C专家编程中》介绍了一种方法用来分析C语言的声明,还是很有效果,不过脑子不好使,老爱忘记,现在把这段秘诀贴在这里:
--------------------------------------------------------------------------
    A: 从变量名字开始读声明,然后按优先级读.
    B: 优先级从高到低是:
        B.1: 声明中()里的部分    #这里是指和变量名在一起的括号里面的部分
        B.2: 后缀操作符:      
             圆括号()标识一个函数原型,   
             括号[]标识一个数组,
        B.3: 前缀操作符:星号*表示“指向...的指针”.
    C: 如果const和(或)volatile关键字的后面紧跟类型说明符(如int,long等), 那么它作用于          类型说明符。在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。
--------------------------------------------------------------------------
    总结一下以上规则就是,找到变量名-->读和变量名在一切的括号里面的东西->读后缀->读前缀。

    最好的方法就是举例来说明这些规则的使用方法。
例1:
    char * const *(*next)(int a, int b);
    按优先级步骤来:
    1) 找变量名 : next
    2) 找括号里面的东西:  *next 
         说明next是一个指向...的指针
    3) 看后缀: (int a, int b)
         说明(*next)是一个函数,和2)连起来就是: next是一个指向有两个参数的函数的指针
    4) 看前缀: char * const *
         说明是一个指向一个字符的指针的常量指针。
    连起来:next是一个指向有两个参数的函数的指针,其返回值是一个指向一个字符的指针的常量指针。

例2:
    char *(*c[10])(int **p)
    解析步骤:
    1) 找变量名: c
    2) 找和变量名在一起的小括号里面的内容: (*c[10])
       读后缀:c是一个数组;读前缀:c是一个指向...的指针;
       连起来:c是一个有10个元素的数组, 每个元素都是指向...的指针。
    3) 读后缀: (int **p)
       c是一个有10个元素的数组,每个元素都是指向函数的指针,函数的参数是int指针的指针类型。
    4) 读前缀: char *
       返回值是字符指针
    c是一个有10个元素的数组,该数组的每个元素是指向函数的指针,该函数的参数是int **,返回值是字符指针。

例3:
    void (*signal(int sig, void(*func)(int)))(int);
    解析步骤:
    1) 变量名: signal
    2) 括号里面的: signal是一个函数,返回值是一个指针
       注意,这里不要把signal当成是一个函数指针了,要首先看变量名后面的括号。
    3) 找后缀:
       signal返回的函数指针,指向的函数参数是int,没有返回值

例4:
    char (*j)[20];
    1) 找变量名: j
    2) 括号里面的: j是一个指向...的指针
    2) 找后缀:
       j是一个指向数组的指针,该数组有20个元素。
    3) 读前缀
       每个元素是char类型。
    j是一个指向数组的指针,该数组内有20个char元素。

例5:
    char *p[20];
    1) 读变量名 :  p
    2) 读后缀 :   p是一个有20个元素的数组
    3) 读前缀 :   p是一个有20个元素的数组,每个元素都是字符指针。

小结: 1) 请注意例4和例5的区别。
      2) 数组前面的修饰符如char *, char 等,修饰的是数组中每个元素。
    比如: char *(*a(int i))[10]
    表示a是一个函数,返回值是一个指针,该指针指向一个有20个元素的数组,每个数组元素是一个字符指针。

2, 关于typedef
    tpyedef是一种声明方式,它为一种类型引入新的名字,而不是为变量分配空间。注意:它没有引入新的类型,而是为现有类型取个别名。
    一般来说,typedef用于简洁的表示复杂的东西。

2.1 定义方法
    char *pa;
    typedef char * string;     //
    string sa, sb;      //声明了两个char* 的变量。

    typedef的定义新别名的方法:
    1) 在定义变量的前面加typedef 如:
       typedef char * pa;
    2) 把变量名,换成你想要使用的别名就可以了:
       typedef char * string;
    3) 使用别名定义其他变量。

    例1:
        int chars[81];
        1) typedef int chars[81]; //第一步
        2) typedef int Line[81];    // 把变量名换成别名
        现在你可以用Line来表示有81个字符的数组了。
        3) Line string1, string2;

2.2 typedef 和 #define 的区别
    根据<>,它们的区别主要表现在两个方面:
    1) 可以用其他类新说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能。
       #define peach int;
       unsigned peach i;   /* ok */
       typedef int ban;
       unsigned ban i;   /* errno */
    2) 在连学的几个变量的声明中,用typedef定义的类型能够保证声明中所有的变量均为同一类型,而用
#define定义的类型无法保证。
       #define int_p int *;
       int_p    pt1, pt2;
       扩展后的形式为:
       int *pt1, pt2;   //这两个变量是一同的类型
    用typedef可以避免这个问题:
       typedef char * p_str;
       p_str ps1, ps2;      //ok, 两个变量都是char *
   
2.3 typedef的用途
    用途一:
       如上面所示,为类型取一个别名。
    用途二:
       和struct配合使用,可以少写几个字,但C专家的作者不推荐这样做,因为struct关键字,可以为你提供一些信息。
       struct point {
          int x;
          int y;
       };
       typedef struct point point;
       这样就可以用point和struct point来定义变量。 但看起来容易混淆,所以建议还是加上struct好些,不要偷懒使用tpyedef。

    用途三:
       用typedef来定义与平台无关的类型。
      比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
      typedef long double REAL;
      在不支持 long double 的平台二上,改为:
      typedef double REAL;
      在连 double 都不支持的平台三上,改为:
      typedef float REAL;
      也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
      标准库就广泛使用了这个技巧,比如size_t。
      另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏>有时也可以完成以上的用途)。

    用途四:
      为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:
      1. 原声明:int *(*a[5])(int, char*);
      变量名为a,直接用一个新别名pFun替换a就可以了:
      typedef int *(*pFun)(int, char*);
      原声明的最简化版:
      pFun a[5];


参考书籍<>
       <>

阅读(1562) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~