原文链接

C语言允许用户使用 typedef 关键字来定义自己习惯的数据类型名称,来替代系统默认的基本类型名称、数组类型名称、指针类型名称与用户自定义的结构型名称、共用型名称、枚举型名称等。一旦用户在程序中定义了自己的数据类型名称,就可以在该程序中用自己的数据类型名称来定义变量的类型、数组的类型、指针变量的类型与函数的类型等。

例如,C 语言在 C99 之前并未提供布尔类型,但我们可以使用 typedef 关键字来定义一个简单的布尔类型,如下面的代码所示:

  1. typedef int BOOL;
  2. #define TRUE 1
  3. #define FALSE 0

定义好之后,就可以像使用基本类型数据一样使用它了,如下面的代码所示:

  1. BOOL bflag=TRUE;

typedef的4种用法

在实际使用中,typedef 的应用主要有如下4种。

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

也就是说,系统默认的所有基本类型都可以利用 typedef 关键字来重新定义类型名,示例代码如下所示:

  1. typedef unsigned int COUNT;

而且,我们还可以使用这种方法来定义与平台无关的类型。比如,要定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型,即:

  1. typedef long double REAL;

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

  1. typedef double REAL;

甚至还可以在连 double 都不支持的平台三上,改为:

  1. typedef float REAL;

这样,当跨平台移植程序时,我们只需要修改一下 typedef 的定义即可,而不用对其他源代码做任何修改。其实,标准库中广泛地使用了这个技巧,比如 size_t 在 VC++2010 的 crtdefs.h 文件中的定义如下所示:

  1. #ifndef _SIZE_T_DEFINED
  2. #ifdef _WIN64
  3. typedef unsigned __int64 size_t;
  4. #else
  5. typedef _W64 unsigned int size_t;
  6. #endif
  7. #define _SIZE_T_DEFINED
  8. #endif

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

以结构体为例,下面我们定义一个名为 Point 的结构体:

  1. struct Point
  2. {
  3. double x;
  4. double y;
  5. double z;
  6. };

在调用这个结构体时,我们必须像下面的代码这样来调用这个结构体:

  1. struct Point oPoint1={100,100,0};
  2. struct Point oPoint2;

在这里,结构体 struct Point 为新的数据类型,在定义变量的时候均要向上面的调用方法一样有保留字 struct,而不能像 int 和 double 那样直接使用 Point 来定义变量。现在,我们利用 typedef 定义这个结构体,如下面的代码所示:

  1. typedef struct tagPoint
  2. {
  3. double x;
  4. double y;
  5. double z;
  6. } Point;

在上面的代码中,实际上完成了两个操作:
1、定义了一个新的结构类型,代码如下所示:

  1. struct tagPoint
  2. {
  3. double x;
  4. double y;
  5. double z;
  6. } ;

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

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

  1. typedef struct tagPoint Point

因此,现在你就可以像 int 和 double 那样直接使用 Point 定义变量,如下面的代码所示:

  1. Point oPoint1={100,100,0};
  2. Point oPoint2;

为了加深对 typedef 的理解,我们再来看一个结构体例子,如下面的代码所示:

  1. typedef struct tagNode
  2. {
  3. char *pItem;
  4. pNode pNext;
  5. } *pNode;

从表面上看,上面的示例代码与前面的定义方法相同,所以应该没有什么问题。但是编译器却报了一个错误,为什么呢?莫非 C 语言不允许在结构中包含指向它自己的指针?

其实问题并非在于 struct 定义的本身,大家应该都知道,C 语言是允许在结构中包含指向它自己的指针的,我们可以在建立链表等数据结构的实现上看到很多这类例子。那问题在哪里呢?其实,根本问题还是在于 typedef 的应用。

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

  1. typedef struct tagNode
  2. {
  3. char *pItem;
  4. struct tagNode *pNext;
  5. } *pNode;

或者将 struct 与 typedef 分开定义,如下面的代码所示:

  1. typedef struct tagNode *pNode;
  2. struct tagNode
  3. {
  4. char *pItem;
  5. pNode pNext;
  6. };

在上面的代码中,我们同样使用 typedef 给一个还未完全声明的类型 tagNode 起了一个新别名。不过,虽然 C 语言编译器完全支持这种做法,但不推荐这样做。建议还是使用如下规范定义方法:

  1. struct tagNode
  2. {
  3. char *pItem;
  4. struct tagNode *pNext;
  5. };
  6. typedef struct tagNode *pNode;

3) 为数组定义简洁的类型名称

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

  1. typedef int INT_ARRAY_100[100];
  2. INT_ARRAY_100 arr;

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

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

  1. typedef char* PCHAR;
  2. PCHAR pa;

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

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

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

  1. // PFun是我们创建的一个类型别名
  2. typedef int *(*PFun)(int,char*);
  3. // 使用定义的新类型来声明对象,等价于int*(*a[5])(int,char*);
  4. PFun a[5];

小心使用 typedef 带来的陷阱

接下来看一个简单的 typedef 使用示例,如下面的代码所示:

  1. typedef char* PCHAR;
  2. int strcmp(const PCHAR,const PCHAR);

在上面的代码中,“const PCHAR” 是否相当于 “const char*” 呢?

答案是否定的,原因很简单,typedef 是用来定义一种类型的新别名的,它不同于宏,不是简单的字符串替换。因此,“const PCHAR”中的 const 给予了整个指针本身常量性,也就是形成了常量指针“char*const(一个指向char的常量指针)”。即它实际上相当于“char*const”,而不是“const char*(指向常量 char 的指针)”。当然,要想让 const PCHAR 相当于 const char* 也很容易,如下面的代码所示:

  1. typedef const char* PCHAR;
  2. int strcmp(PCHAR, PCHAR);

其实,无论什么时候,只要为指针声明 typedef,那么就应该在最终的 typedef 名称中加一个 const,以使得该指针本身是常量。

还需要特别注意的是,虽然 typedef 并不真正影响对象的存储特性,但在语法上它还是一个存储类的关键字,就像 auto、extern、static 和 register 等关键字一样。因此,像下面这种声明方式是不可行的:

  1. typedef static int INT_STATIC;

不可行的原因是不能声明多个存储类关键字,由于 typedef 已经占据了存储类关键字的位置,因此,在 typedef 声明中就不能够再使用 static 或任何其他存储类关键字了。当然,编译器也会报错,如在 VC++2010 中的报错信息为“无法指定多个存储类”。

更多阅读:

关于typedef的用法总结

typedef用法总结之续

C语言typedef详解的更多相关文章

  1. 深入理解C语言 - 指针详解

    一.什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址.CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位.这里,数据对象是指存储在 ...

  2. c++、Java、python对应的编译型语言和解释性语言区别详解

    1.首先明确一点: 高级语言是不能直接在CPU上运行的.CPU只能处理机器语言,就是黑客帝国里面那个10101010101110的数字流. 那么为了让机器语言能够在CPU上运行,那么就必须将其变成机器 ...

  3. (六)C语言之typedef详解

    1.typedef可以看作type define的缩写,顾名思义就是类型定义,也就是说它只是给已有的类型重新定义了一个方便使用的别名,并没有产生新的数据类型.typedef的使用与宏定义define有 ...

  4. C语言指针详解

    前言 这不是我第一次写关于C指针的文章了,只是因为指针对于C来说太重要,而且随着自己编程经历越多,对指针的理解越多,因此有了本文. 为什么需要指针? 指针解决了一些编程中基本的问题. 第一,指针的使用 ...

  5. C语言正则表达式详解 regcomp() regexec() regfree()详解

    标准的C和C++都不支持正则表达式,但有一些函数库可以辅助C/C++程序员完成这一功能,其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库 ...

  6. C语言关键字详解

    相对于其他语言来说,C语言的关键字算是少的了.在C98中关键子总共只有32个,我们来分析一下每个关键字在C语言中它独特的作用. 1.关于数据类型的关键字 (1) char :声明字符型变量或函数  ( ...

  7. C语言文件详解

    1.C语言FILE类,在stdio.h头文件中,FILE类是一个结构体:定义如下: 通过typedef定义了 文件类型 的别名: “FILE”,这样以后需要读写文件的时候直接定义FILE就行了.   ...

  8. c/c++中typedef详解

    1. typedef 最简单使用 typedef long byte_4; // 给已知数据类型long起个新名字,叫byte_4 你可以在任何需要 long 的上下文中使用 byte_4.注意 ty ...

  9. 小程序脚本语言WXS详解

    WXS脚本语言是 Weixin Script脚本的简称,是JS.JSON.WXML.WXSS之后又一大小程序内部文件类型.截至到目前小程序已经提供了5种文件类型. 解构小程序的几种方式,其中一种方式就 ...

随机推荐

  1. 5.Hiveguigun滚(ノ`Д)ノ竟然竞争谨慎谨慎谨慎哈喇子罢工八公

    1.Hive简介 2.Hive部署与安装 3.Hive的使用 4.Hive JDBC编程

  2. TensorFlow 学习(3)——MNIST机器学习入门

    通过对MNIST的学习,对TensorFlow和机器学习快速上手. MNIST:手写数字识别数据集 MNIST数据集 60000行的训练数据集 和 10000行测试集 每张图片是一个28*28的像素图 ...

  3. vue路由在keep-alive下的刷新问题 对watch的影响

    转载自:https://www.cnblogs.com/dansingal/p/8302100.html 问题描述: 在keep-alive中的在跳转到指定的路由时刷新对应的路由,其余不刷新. 1 2 ...

  4. CSS中盒子模型

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. AXIS 1.4 自定义序列化/反序列化类

    axis1.4的CalendarDeserializer 使用的时区是GMT,导致日期显示不准确 private static SimpleDateFormat zulu = new SimpleDa ...

  6. 学习Oracle数据库入门到精通教程资料合集

    任何大型信息系统,都需要有数据库管理系统作为支撑.其中,Oracle以其卓越的性能获得了广泛的应用.本合集汇总了学习Oracle数据库从入门到精通的30份教程资料. 资料名称 下载地址 超详细Orac ...

  7. 获取packageName和startActivity

    import android import pprint # 获取packageName droid = android.Android(('192.168.1.101', 42250)) droid ...

  8. Angular引入第三方库

    原文已经写的很好了.原文链接: https://blog.csdn.net/yuzhiqiang_1993/article/details/71215232 加上2点给自己用,引入bootstrap样 ...

  9. Ceph osd故障硬盘更换

    正常状态: 故障状态: 实施更换步骤: (1)关闭ceph集群数据迁移: osd硬盘故障,状态变为down.在经过mod osd down out interval 设定的时间间隔后,ceph将其标记 ...

  10. 【学习笔记】python3中csv文件使用

    1. reader=csv.reader(f, delimiter=','):按行读取数据,reader为生成器,读取的每行数据为列表格式,可以通过delimiter参数指定分隔符. import c ...