typedef和#define的区别

来源:互联网 发布:网络进度计划要点 编辑:程序博客网 时间:2024/06/03 03:51

typedef的用法

typedef的意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别名,在这一点上与引用的含义类似,引用是变量或对象的别名,而typedef定义的是类型的别名。typedef不是定义一个新的数据类型。
typedef的主要用途有
1、定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:

char* pa,pb; 

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

以下则可行:

typedef char* PCHAR;PCHAR pa,pb;  

这种用法很有用,特别是char* pa, pb的定义,初学者往往认为是定义了两个字符型指针,其实不是,而用typedef char* PCHAR就不会出现这样的问题,减少了错误的发生。
2、与struct的结合使用
在C++中,struct与class的作用相同,就是默认的访问权限不同,struct默认为public,而class默认为private的。并且struct可以直接通过结构体类型名来定义变量。

struct Person  {      string name;      int age;  };  Person person; 

而在C语言中,声明struct新对象时,必须带上struct,其形式为:

struct 结构体类型名 变量名;

例如:

struct Person  {      char *name;      int age;  };  struct Person person;  

所以在C语言中struct的定义和声明要用typedef,可以和C++一样通过结构体类型名(别名)直接定义对象。
例1:

typedef struct __Person  {      char *name;      int age;  }Person;    //这是Person是结构体的一个别名  Person person; 

例2:

typedef struct student{    char *name;    int age;}Stu_st,*Stu_pst;

A)struct student stu1;Stu_st stu1;没有区别
B)struct student *stu2;Stu_pst *stu2;没有区别
我们把struct student{/*code*/}struct student{/*code*/}*都当做一个整体,只是给它们起了一个别名分别是Stu_st,Stu_pst而已。
C)const Stu_pst stu3;和Stu_pst const stu4的作用一样,指针本身的值可以改,而指向的值不可以改;相当于int const a;和const int b的作用一样。Stu_pst是struct student{/*code*/}* 的别名,struct student{/*code*/}* 是一个整体,对于编译器来说,只认为Stu_pst是一个类型名,所以在解析的时候很自然把Stu_pst这个数据类型名忽略掉了。

3、定义与平台无关的类型
定义与平台无关的类型,屏蔽不同平台的类型差异化如用typedef来定义与平台无关的类型。

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

typedef long double REAL;

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

typedef double REAL;

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

typedef float REAL;

也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健。
4、简化复杂的类型声明
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:
(1) 原声明:声明a是指向函数参数为(int,char *)、返回值类型为整型指针的函数的含5个函数指针的指针数组。(简单说,就是函数指针数组,即数组的元素是函数指针)

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) 原声明:声明e是含有9个元素的数组的指针,元素类型为返回值为double类型的函数的指针。

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 (*)[]数组指针

typedef使用的两个陷阱
(1)陷阱一:与const一起使用
记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:
先定义:

typedef char* PSTR;

然后:

int mystrcmp(const PSTR, const PSTR);

const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const,因为编译器把PSTR当做一个整体的类型来看待,const给予了整个指针本身以常量性,也就是形成了常量指针char* const
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。
例如:

#include <stdio.h>typedef char* CHARS;typedef CHARS const CPTR_1;typedef const CHARS CPTR_2;typedef char* const CPTR_3;typedef const char* CPTR_4;int main(){    CHARS str = "hello world";    printf("str:%s\n",str);    CPTR_1 str2 = "Are you ok?";//  str2 = str;    printf("str2:%s\n",str2);    CPTR_2 str3 = "I'm fine.";//  str3 = str;    printf("str3:%s\n",str3);    CPTR_3 str4 = "haha,who are you?";//  str4 = str;    printf("str4:%s\n",str4);    CPTR_4 str5 = "I am Ro";    str5 = str;    printf("str5:%s\n",str5);    return 0;}

该函数中,只有CPTR_4定义的变量str5可以被修改,即可以修改本身的值。
(2)陷阱二:与存储类的关键字一起使用
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:

typedef  static  int  INT2; //不可行 

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

#define的用法

#define (宏定义)是预处理指令,它在编译预处理时进行简单的替换,不作正确性检查。它可以出现在代码的任何地方,从本行宏定义开始,以后的代码就都认识这个宏了,可以把任何东西定义已成宏。
例如:
(1)数值宏常量

#define PI 3.141492654//定义π#define ERROR_POWEROFF -1//定义错误

(2)字符串宏常量

#define ENG_PATH_1 E:\\English\\listen_to_this\\listen_to_this_3//定义路径

(3)宏定义注释符号?

#define BSC //#define BMC /*#define EMC */

A)BSC my single-line comment
B)BMC my multi-line comment EMC
A)和B)都错误,因为注释先于预处理指令被处理,当这两行被展成//…或/*...*/ 时,注释已处理完毕,此时再出现//…或者/*...*/ 自然错误,试图用宏开始或结束一段注释是不行的。
(4)宏表达式

#define SEC_A_YEAR 60*60*24*365

(5)宏函数

#define SQR(x) ((x)*(x))#defien SUM(x) ((x)+(x))

宏函数的中,为防止出错,所以表达式的符号一般不要省略。宏函数被调用时是以实参换形参,而不是“值传送”。
注意:宏定义的使用缺陷
例如:

#define MYFUNC(a,b) ((a) < (b) ? (a) : (b))int myfunc(int a,int b){    return a < b ? a : b;}void main(){    int a = 1;    int b = 3;//  int c = myfunc(++a,b);//2    //带参数的宏和内联函数的区别    int c = MYFUNC(++a,b);// ((++a) < (b) ? (++a):(b))    printf("a = %d\n",a);    printf("b = %d\n",b);    printf("c = %d\n",c);}

原来想要的结果是:
a = 2
b = 3
c = 2
而使用宏定义后结果却是:
a = 3
b = 3
c = 3

(6)宏定义中的空格

#define SUM (x) (x)+(x)

这已经不是宏函数SUM(x)了。编译器认为这是定义了一个宏:SUM,其代表的是 (x) (x)+(x),关键问题在于SUM后面的这个空格。这个空格仅仅在定义的时候有效,在使用这个宏函数的时候,空格会被编译器忽略。即就是,定义好的宏函数SUM(x)在使用的时候在SUM和(x)之间留有空格是没有问题的。
(7)卸载宏(撤销宏定义)
#undef 是用来撤销宏定义的。用法如下:

#define PI 3.141592654...//code#undef PI

也就是说,宏的生命周期从#define开始到#undef结束。
如:

#define X 3#define Y X*2#undef X#define X 2int z=Y;

z的值是4。宏定义要在使用之前定义。

typedef和#define的区别

1、执行时间不同

关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。
#define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。

例1:typedef会做相应的类型检查:

#include <iostream>typedef unsigned int UINT;  void main()  {      UINT value = "abc"; // error: invalid conversion from `const char*' to `UINT'    cout << value << endl;  }  

例2:#define不做类型检查:

#include <stdio.h>#define f(x) x*xint main()  {      int a=6, b=2, c;      c=f(a) / f(b);      printf("%d\n", c);      return 0;  }  

程序的输出结果是: 36,根本原因就在于#define只是简单的字符串替换。
2、 功能有差异
(1)typedef用来定义类型的别名,定义与平台无关的数据类型,与struct的结合使用等。
(2)#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
3、作用域不同
#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。而typedef有自己的作用域。
举例:
【例1】#define没有作用域的限制,只要是之前预定义过就可以

#include <iostream>#include <string>void func()  {      #define HW "HelloWorld";  }  void main()  {      string str = HW;      cout << str << endl;  }  

输出结果是:
HelloWorld

【例2】 而typedef有自己的作用域

void func1()  {       typedef unsigned int UINT;  }  void func2()  {       UINT uValue = 5;//error C2065: “UINT”: 未声明的标识符}  

【例3】

class A  {          typedef unsigned int UINT;          UINT valueA;          A() : valueA(0){}  };  class B  {          UINT valueB;          //error C2146: 语法错误 : 缺少“;”(在标识符“valueB”的前面)};  

上面例子在B类中使用UINT会出错,因为UINT只在类A的作用域中。此外,在类中用typedef定义的类型别名还具有相应的访问权限
【例4】:

class A  {         typedef unsigned int UINT;         UINT valueA;         A() : valueA(0){}  };  void func3()  {         A::UINT i = 1;         //  error C2248: “A::UINT”: 无法访问 private typedef(在“A”类中声明)}  

而给UINT加上public访问权限后,则可编译通过。
【例5】:

class A  {  public:         typedef unsigned int UINT;         UINT valueA;         A() : valueA(0){}  };  void func3()  {         A::UINT i = 1;         cout << i << endl;  } 

4、对指针的操作
二者修饰指针类型时,作用不同。
举例:

#include <iostream>typedef int * pint;#define PINT int *int i1 = 1, i2 = 2;const pint p1 = &i1;    //p不可更改,p指向的内容可以更改,相当于 int * const p;const PINT p2 = &i2;    //p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;pint s1, s2;    //s1和s2都是int型指针PINT s3, s4;    //相当于int * s3,s4;只有一个是指针。void main(){    cout << "p1:" << p1 << "  *p1:" << *p1 << endl;//  p1 = &i2;   //error C3892: “p1”: 不能给常量赋值    *p1 = 5;    cout << "p1:" << p1 << "  *p1:" << *p1 << endl;    cout << "p2:" << p2 << "  *p2:" << *p2 << endl;//  *p2 = 10;   //error C3892: “p2”: 不能给常量赋值    p2 = &i1;    cout << "p2:" << p2 << "  *p2:" << *p2 << endl;}

输出结果:
p1:008BC004 *p1:1
p1:008BC004 *p1:5
p2:008BC008 *p2:2
p2:008BC004 *p2:5

参考:http://www.cnblogs.com/kerwinshaw/archive/2009/02/02/1382428.html
http://blog.csdn.net/luoweifu/article/details/41630195