C语言-入门-typedef(十八)

x33g5p2x  于2022-08-17 转载在 其他  
字(3.3k)|赞(0)|评价(0)|浏览(277)

前沿

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

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

typedef int BOOL;
#define TRUE 1
#define FALSE 0

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

BOOL bflag=TRUE;

定义

格式: typedef 原类型名 新类型名;

  • 其中原类型名中含有定义部分,新类型名一般用大写表示,以便于区别。
  • 有时也可用宏定义来代替typedef的功能,但是宏定义是由预处理完成的,而typedef则是在编译 时完成的,后者更为灵活方便。

typedef的4种用法

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

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

typedef unsigned int COUNT;

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

typedef long double REAL;

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

typedef double REAL;

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

typedef float REAL;

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

#ifndef _SIZE_T_DEFINED
#ifdef  _WIN64
typedef unsigned __int64    size_t;
#else
typedef _W64 unsigned int   size_t;
#endif
#define _SIZE_T_DEFINED
#endif

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

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

struct Point
{
    double x;
    double y;
    double z;
};

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

struct Point oPoint1={100,100,0};
struct Point oPoint2;

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

//定义并起别名
typedef struct tagPoint
{
    double x;
    double y;
    double z;
} Point; 

//或者在定义后在起别名
typedef struct tagPoint Point

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

Point oPoint1={100,100,0};
Point oPoint2;

我们再来看一个结构体例子,如下面的代码所示:

typedef struct tagNode
{
    char *pItem;
    pNode pNext;//这里会报错
} *pNode;

从表面上看,上面的示例代码与前面的定义方法相同,所以应该没有什么问题。但是编译器却报了一个错误,为什么呢?莫非 C 语言不允许在结构中包含指向它自己的指针?
其实问题并非在于 struct 定义的本身,大家应该都知道,C 语言是允许在结构中包含指向它自己的指针的,我们可以在建立链表等数据结构的实现上看到很多这类例子。那问题在哪里呢?其实,根本问题还是在于 typedef 的应用。

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

typedef struct tagNode *pNode;
struct tagNode
{
    char *pItem;
    pNode pNext;
};

不过,虽然 C 语言编译器完全支持这种做法,但不推荐这样做。建议还是使用如下规范定义方法:

struct tagNode
{
    char *pItem;
    struct tagNode *pNext;
};
typedef struct tagNode *pNode;

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

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

typedef int INT_ARRAY_100[100];
INT_ARRAY_100 arr;

为指针定义简洁的名称

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

typedef char* PCHAR;
PCHAR pa;

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

int pringstr(int a,char * b){
    printf("%d,%s",a,b);
    return 1;
}

int main(int argc, char* argv[])
{
    int *(*a[5])(int,char*);
    a[0]=pringstr(1,"2");
    a[0];
    return 0;
}

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

typedef int *(*PFun)(int,char*);
int pringstr(int a,char * b){
    printf("%d,%s",a,b);
    return 1;
}

int main(int argc, char* argv[])
{   // 使用定义的新类型来声明对象,等价于int*(*a[5])(int,char*);
    PFun a[5];
    a[0]=pringstr(1,"2");
    a[0];
    return 0;
}

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

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

typedef和宏定义区别

原理不同
#define是C语言中定义的语法,是预处理指令,在预处理时进行简单而机械的字符串替换,不作正确性检查,只有在编译已被展开的源程序时才会发现可能的错误并报错。
typedef是关键字,在编译时处理,有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名,但不能在一个函数定义里面使用typedef。用typedef定义数组、指针、结构等类型会带来很大的方便,不仅使程序书写简单,也使意义明确,增强可读性。

功能不同
typedef用来定义类型的别名,起到类型易于记忆的功能。另一个功能是定义机器无关的类型。如定义一个REAL的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL, 在不支持long double的机器上,看起来是这样的,typedef double REAL,在不支持double的机器上,是这样的,typedef float REAL
#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

作用域不同
#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而typedef有自己的作用域。

相关文章