typedef的用法

1) 为基本数据类型定义新的类型名

  当跨平台移植程序时,我们只需要修改一下 typedef 的定义即可,而不用对其他源代码做任何修改。

eg:不同平台下表示最高精度

  A、typedef long double REAL;

  B、typedef double REAL;

  C、typedef float REAL;

2) 为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称

eg

  typedef struct tagPoint

  {

      double x;

      double y;

      double z;

  } Point;

上述完成了两个操作:

1、定义了一个新的结构类型,代码如下所示:

  struct tagPoint

  {

      double x;

      double y;

      double z;

  } ;

其中,struct 关键字和 tagPoint 一起构成了这个结构类型,无论是否存在 typedef 关键字,这个结构都存在。

2、使用 typedef 为这个新的结构起了一个别名,叫 Point,即:

  typedef struct tagPoint Point

  此时可以直接使用 Point 定义变量,如下面的代码所示:

  Point oPoint1={100,100,0};

  Point oPoint2;

补充:

  typedef struct tagNode

  {

      char *pItem;

      pNode pNext;

  } *pNode;

  上面的示例代码编译器会报了一个错误,为什么呢?(C 语言是允许在结构中包含指向它自己的指针的)报错的根本问题在于 typedef 的应用。

  在上面的代码中,新结构建立的过程中遇到了 pNext 声明,其类型是 pNode。这里要特别注意的是,pNode 表示的是该结构体的新别名。于是问题出现了,在结构体类型本身还没有建立完成的时候,编译器根本就不认识 pNode,因为这个结构体类型的新别名还不存在,所以自然就会报错。因此,我们要做一些适当的调整,比如将结构体中的 pNext 声明修改成如下方式:

  typedef struct tagNode

  {

      char *pItem;

      struct tagNode *pNext;

  } *pNode;

或者

  struct tagNode

  {

      char *pItem;

      struct tagNode *pNext;

  };

  typedef struct tagNode *pNode;

3):简化,提高可读性

  它的定义方法很简单,与为基本数据类型定义新的别名方法一样,示例代码如下所示:

  typedef int INT_ARRAY_100[100];

  INT_ARRAY_100 arr;

  补充说明:

  typedef int array[10],这个代表什么意思,这可不是平时那种array[10]是个int了吧,这个代表,array是个重定义的类型,array a; 就代表a是一个含有10个int型元素的数组,这么定义可能觉得简化不了多少,还不如直接int a[10];看着明白呢,好! 继续往下看,typedef array Array[5]; 这个呢,如果我这么定义一个变量 Array arr[2]; 这个又是什么意思呢?这个就是定义个Array类型数组,其中这个“一维”数组有两个元素,但是每个元素又都是Array 类型的,每个Array又是一个5行10列二维数组,所以arr就是一个三维数组,他就等于int arr[2][5][10]; 是不是看的有点蒙了, 其实在C语言中根本不存在多维数组,全部都是一维数组,只是一维数组里面又含一维数组,所以才构成了所谓的“多维”数组, 如果这样理解的话,是不是觉得多维数组也就不那么“神奇”了,变得简单了,没有那么晕了,这样定义的好处就是可以讲一个多维的数组转化成我们比较熟悉的一维数组,这样操作起来就比较容易了,可读性自然也会增强。所以这就是他的“简化”的作用。其实我们还可以在函数指针上体现,想定义个函数指针int (*p)(); 我们可以定义为typedef int (*POWER)(); 接下来可以直接定义POWER p1, p2; 他就等价于int (*p1)(), int (*p2)(); 这样也非常易懂。

4) 为指针定义简洁的名称

  对于指针,我们同样可以使用下面的方式来定义一个新的别名:

  typedef char* PCHAR;

  PCHAR pa;

  对于上面这种简单的变量声明,使用 typedef 来定义一个新的别名或许会感觉意义不大,但在比较复杂的变量声明中,typedef 的优势马上就体现出来了,如下面的示例代码所示:

  int *(*a[5])(int,char*);

  对于上面变量的声明,如果我们使用 typdef 来给它定义一个别名,这会非常有意义,如下面的代码所示:

  // PFun是我们创建的一个类型别名

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

  // 使用定义的新类型来声明对象,等价于int*(*a[5])(int,char*);

  PFun a[5];

补充:

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

  从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:

  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。

两个模式:

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

  type (*)[]数组指针

5)、两大陷阱

陷阱一:

  记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:

  先定义:

  typedef char* PSTR;

  然后:

  int mystrcmp(const PSTR, const PSTR);

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

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

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

陷阱二:

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

  typedef static int INT2; //不可行

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

posted @ 2019-04-30 09:16  晓不小QAQ  阅读(871)  评论(0编辑  收藏  举报