typedef详解以及与宏定义#define的区别

发布于:2021-06-20 13:12:00

typedef是在计算机编程语言中用来为复杂的声明定义简单的别名,它与宏定义有些差异。它本身是一种存储类的关键字,与auto、extern、mutable、static、register等关键字不能出现在同一个表达式中;
typedef 中文解释:声明类型


typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)


在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。


主要用法:
1、创建*台无关的数据类型,隐藏笨拙且难以理解的语法


typedef int size;
void measure(size*psz);
size array[4];

已知数据类型long起个新名字byte_4


typedef long byte_4;

2、掩饰复合类型,如指针和数组
下面这样重复定义有 81 个字符元素的数组:


char line[81];
char text[81];

定义,Line类型即代表了具有81个元素的字符数组,使用方法如下:


typedef char Line[81];
Line text,line;

typedef int array[2];

array等价于 int [2]定义;
array a声明等价于int a[2]声明


typedef int array[M][N];

array等价于 int [M][N]定义;
array a声明等价于int a[M][N]声明


隐藏指针语法:


typedef char* pstr;
int mystrcmp(const pstr p1,const pstr p3);

eg:


typedef int *pointer;

pointer等价于 int *定义;
pointer p声明等价于int *p声明


eg:


typedef int *pointer[M];

pointer等价于 int *[M]定义;
pointer p声明等价于int *p[M]声明


3、typedef与结构结合使用


typedef struct tagMyStruct
{
int iNum;
long lLength;
}MyStruct;

这语句实际上完成两个操作:
(1)定义一个新的结构类型


struct tagMyStruct
{
int iNum;
long lLength;
};

tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
(2)typedef为这个新的结构起了一个名字,叫MyStruct。


typedef struct tagMyStruct MyStruct;

因此,MyStruct实际上相当于struct tagMyStruct,可以使用MyStruct varName来定义变量。


4、下面的代码定义一个结构时,编译器报了一个错误,莫非C语言不允许在结构中包含指向它自己的指针吗?


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

分析:
C语言当然允许在结构中包含指向它自己的指针,上述代码的根本问题在于typedef的应用。新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。
解决这个问题的方法有多种:
(1)


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

(2)


typedef struct tagNode* pNode;
struct tagNode
{
char* pItem;
pNode pNext;//这边不用pNode* ,pNode 已经表示了struct tagNode*
};

注意:在这个例子中,typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法,但是不推荐这种写法。
(3)


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

5、函数声明


typedef int func(void);

func等价于 int (void)类型函数;
func f声明等价于 int f(void)声明,用于文件的函数声明;
func *pf声明等价于 int (*pf)(void)声明,用于函数指针;


6、函数指针


typedef int (*func)(void)

func等价于int (*)(void)类型;
func pf等价于int (*pf)(void)声明,pf是一个函数指针变量


7、 typedef & #define的区别

#define只是简单的字符串替换而typedef则是为一个类型起新名字。
一般来说,typedef要比#define要好,特别是在有指针的场合。


typedef char* pStr1;
#define pStr2 char* 
pStr1 s1,s2;
pStr2 s3,s4;

在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是所预期的指针变量。要达到所预期的效果,上例中define语句必须写成 pStr2 s3, *s4; 这样才能正常执行。


8、#define用法陷阱


#include
#define f(x) x*x
int main(void)
{
int a=6, b=2, c;
c = f(a) / f(b);
printf("%d
", c);
return 0;
}

程序的输出结果是: 66/22=36,并不是预期的(66)/(22)=9;
如此原因,在许多C语言编程规范中提到 使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:


#definef(x)((x)*(x))

9、typedef用法陷阱


typedef char *pStr;
char string[4]="abc";
const char *p1=string;
const pStr p2=string;
p1++;
p2++;

上述p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和pStr const p2本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此 const pStr p2的含义是,限定数据类型为char *的变量p2为只读 ,因此p2++错误。


10、 #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。


在C或C++语言中,“宏”分为有参数和无参数两种。
(1)无参宏的宏名后不带参数。


#define 标识符 字符串

其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。


#define M (a+b)

注意:宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。
宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令


(2)c语言允许宏带有参数。
在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。


#define 宏名(形参表) 字符串
#define M(y) ((y)*(y)+3*(y)) /*宏定义*/
....
k=M(5); /*宏调用*/
....

在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:
k=5*5+3*5


(3)防止重复定义


#ifndef __headerfileXXX__
#define __headerfileXXX__

文件内容

#endif

(4)简单定义


#define MAXTIME 1000

(5)条件编译-1


#ifdef WINDOWS
......
......
#endif
#ifdef LINUX
......
......
#endif

(6)条件编译-2


#ifdef XXX…(#else) … #endif

eg:


#ifdef AUX_INPUT
#define AUX_MODE 3
#else
#define AUY_MODE 3
#endif

11、 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。


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

这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:


PF Register(PF pf);

Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做下面展示一下如果不用 typedef,我们是如何实现这个声明的:


int (*Register (int (*pf)(const char *, const char *)))(const char *, const char *);

很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:“OK,有人还会写这样的代码吗?”,快速浏览一下揭示 signal()函数的头文件 ,一个有同样接口的函数。


注意这里Register被定义为一个函数而不是函数指针,如果要定义为函数指针应该这样写:


int (*(*Register) (int (*pf)(const char *, const char *))) (const char *, const char *);

12、ypedef 另外一个重要的用途,那就是定义机器无关的类型
定义一个叫 REAL 的浮点类型,在目标机器上它可以获得最高的精度:


typedef long double REAL;

在不支持 long double 的机器上,该 typedef 看起来会是下面这样:


typedef double REAL;

并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:


typedef float REAL;

你不用对源代码做任何修改,便可以在每一种*台上编译这个使用 REAL 类型的应用程序。
唯一要改的是 typedef 本身。
标准库广泛地使用 typedef 来创建这样的*台无关类型


13、结构体有无typedef的区别


struct node{
datatype data;
struct node *lchild,*rchild;
}bintnode;

上述代码相当于:


struct node{
datatype data;
struct node *lchild,*rchild;
};
struct node bintnode;

一个类型struct node的完整描述,并定义了一个struct node类型的对象bintnode。


typedef struct node{
datatype data;
struct node *lchild,*rchild;
}bintnode;

上述代码相当于:


struct node{
datatype data;
struct node *lchild,*rchild;
};
typedef struct node bintnode;

定义了一个类型别名bitnode,实际上指的就是struct node这个完整类型.typedef定义的类型别名在作用域内和被定义的原类型语义上等价,都是表示同一个类型的名称。这里typedef之后bitnode可以和struct node互相代替。

相关推荐

最新更新

猜你喜欢