typedef用来声明一个别名,typedef关键字后面是一个声明。语法上typedef属于存储类声明说明符


一、引言

  如果你理解typedef的简单形式:

typedef int PARA; 这种形式跟#define int PARA几乎一样。

  那么你是否也理解了如下的一些声明:

    typedef int a[10]; a[10]是int的别名吗??

     typedef void (*p)(void); (*p)(void)是void的别名吗??

  以为a[10]是int的别名,(*p)(void)是void的别名,看起来又似乎不是合法的名字

  实际上,上面的语句把a声明为具有10个int元素的数组的类型别名,p是一种函数指针的类型别名。 虽然在功能上,typedef可以看作一个跟int PARA分离的动作,但语法上typedef属于存储类声明说明符,因此严格来说,

  typedef int PARA 整个是一个完整的声明。

  在定义一个函数指针类型。比如原函数是 void func(void);

  那么定义的函数指针类型就是 typedef void (*Fun)(void);

  然后用此类型生成一个指向函数的指针: Fun func1;

  当func1获取函数地址之后,那么你就可以向调用原函数那样来使用这个函数指针: func1(void);

void func(void);

typedef void (*Fun)(void);

Fun func1;

func1(void);

二、用途

  1.  定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象

    char* pa, pb;  // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针和一个字符变量;

  以下则可行:

    typedef char* PCHAR; // 一般用大写

    PCHAR pa, pb; // 可行,同时声明了两个指向字符变量的指针

  虽然:

    char *pa, *pb;

  也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。

  2.  用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名 对象名

    struct tagPOINT1{

      int x;

      int y;

    };

    struct tagPOINT1 p1;

  而在C++中,则可以直接写:结构名 对象名,即:

    tagPOINT1 p1;

  估计某人觉得经常多写一个struct太麻烦了,于是就发明了:

    typedef struct tagPOINT{

      int x;

      int y;

    }POINT;

    POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候

  或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。

  3.  用typedef来定义与平台无关的类型。

  比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:

    typedef long double REAL;

  在不支持 long double 的平台二上,改为:

    typedef double REAL;

  在连 double 都不支持的平台三上,改为:

    typedef float REAL;

  也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。

标准库就广泛使用了这个技巧,比如size_t。

  另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。

  4. 为复杂的声明定义一个新的简单的别名。

  方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版

  (1). 原声明:int *(*a[5])(int, char*);

    变量名为a,直接用一个新别名pFun替换a就可以了:

    typedef int *(*pFun)(int, char*);

    原声明的最简化版:

    pFun a[5];

  (2). 原声明:void (*b[10]) (void (*)());

    变量名为b,先替换右边部分括号里的,pFunParam为别名一:

    typedef void (*pFunParam)();

    再替换左边的变量b,pFunx为别名二:

    typedef void (*pFunx)(pFunParam);

    原声明的最简化版:

    pFunx b[10];

  (3). 原声明:doube(*)() (*e)[9];

    变量名为e,先替换左边部分,pFuny为别名一:

    typedef double(*pFuny)();

    再替换右边的变量e,pFunParamy为别名二

    typedef pFuny (*pFunParamy)[9];

    原声明的最简化版:

    pFunParamy e;

    理解复杂声明可用的“右左法则”:

      从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;

      括号内分析完就跳出括号,还是按先右后左的顺序,

      如此循环,直到整个声明分析完

    int (*func)(int *p);

      首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;

      然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。

    int (*func[5])(int *);

      func右边是一个[]运算符,说明func是具有5个元素的数组;

      func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰 func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。

      跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。

  也可以记住2个模式:

    type (*)(….)          函数指针

    type (*)[]          数组指针


三、陷阱

  1. typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。

  定义:typedef char* PSTR;

     int mystrcmp(const PSTR, const PSTR);

    const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。

  原因: 在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。

  简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

  2. typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性。

  typedef static int INT2;    //不可行

  编译将失败,会提示“指定了一个以上的存储类”。


四、typedef 和 #define

  1、typedef的用法

    在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像:

    typedef int INT;

    typedef int ARRAY[10];

    typedef (int*) pINT;

    typedef 可以增强程序的可读性,以及标识符的灵活性,但它也有“非直观性”等缺点。

  2、#define的用法

    #define为一宏定义语句,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,它本身并不在编译过程中进行,而是在这之前(预处理过程)就已经完成了,但也因此难以发现潜在的错误及其它代码维护问题:

    #define INT int

    #define TRUE 1

    #define Add(a,b) ((a)+(b));

    #define Loop_10 for (int i=0; i<10; i++)

    在Scott Meyer的Effective C++一书的条款1中有关于#define语句弊端的分析,以及好的替代方法,大家可参看。

  3、typedef与#define的区别

    从以上的概念便也能基本清楚,

    typedef : 只是为了增加可读性而为标识符另起的新名称(仅仅只是个别名),

    #define :原本在C中是为了定义常量,到了C++,const、enum、inline的出现使它也渐渐成为了起别名的工具。

    有时很容易搞不清楚与typedef两者到底该用哪个好,如#define INT int这样的语句,用typedef一样可以完成,用哪个好呢? 一般都遵循#define定义“可读”的常量以及一些宏语句的任务,而typedef则常用来定义关键字、冗长的类型的别名。

  宏定义只是简单的字符串代换(原地扩展),而typedef则不是原地扩展,它的新名字具有一定的封装性,以致于新命名的标识符具有更易定义变量的功能。请看上面第一大点代码的第三行:

    typedef (int*) pINT;

  以及下面这行:

    #define pINT2 int*

  效果相同 ? 实则不同 ! 实践中见差别:pINT a,b;的效果同int *a; int *b;表示定义了两个整型指针变量。而pINT2 a,b;的效果同int *a, b;表示定义了一个整型指针变量a和整型变量b。



typedef int (funFactory)();
typedef int (*pfunFactory)();

int f(){
    printf(".....f()...\n");
}

int main(){
  funFactory* pf = f;
  pf();

  pfunFactory ppf = f;
  ppf();
}

如上使用typedef 两种不同的函数定义方法,可以更好的证实typedef 完成了类型别名的定义。

最新文章

  1. DevExpress第三方控件使用实例之ASPxPopupControl弹出子窗体
  2. 在webapp上使用input:file, 指定capture属性调用默许相机,摄像,录音功能
  3. L#脚本语言,直接把DLL当脚本执行(图解说明)
  4. iOS 学习笔记 七 (2015.03.29)code snippet操作
  5. Cadence封装制作之表贴封装的制作
  6. CentOS 6.4 编译Open vSwitch2.0
  7. MySQL binlog_rows_query_log_events
  8. new关键字和newInstance()方法的区别
  9. Delphi安装NT服务程序时(不出现提示信息)
  10. js html 交互监听事件学习
  11. elasticsearch基础概念
  12. SparkR安装部署及数据分析实例
  13. Sqlserver2014 Master....提示异常,IIS未安装
  14. JAVA微信公众号网页开发 —— 接收微信服务器发送的消息
  15. spring-boot-2.0.3启动源码篇五 - run方法(四)之prepareContext
  16. Git:非Fast forward下的合并(--no-ff方式的git merge)
  17. 解决“chrome adobe flash player不是最新版本”的方法
  18. 移动环境下DNS解析失败后的优化方案
  19. Ceph 分布式存储
  20. with as 和update ,Delete,insert

热门文章

  1. css高级
  2. Linear Algebra lecture6 note
  3. WP8应用上传失败查错
  4. (转) Awesome Deep Learning
  5. 3、C#入门第3课
  6. Linux-磁盘及网络IO工作方式解析
  7. updatepanel 回发或回调参数无效
  8. svn提交报e200007错误
  9. Web前端MVC框架
  10. C#遍历指定文件夹中的所有文件和子文件夹