typedef和#define的区别
2017-10-15 19:21
204 查看
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 2 int 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*x int 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
相关文章推荐
- typedef和#define的用法与区别
- typedef和#define的用法与区别等
- typedef和#define的用法与区别
- typedef和#define的用法与区别
- typedef和#define的用法与区别
- typedef和#define究竟有什么区别?
- #define与typedef的区别
- typedef和#define的用法与区别
- #define和typedef的区别
- typedef 和#define的简单区别
- #define、const、typedef的区别
- typedef和#define的用法与区别
- typedef与#define区别
- typedef与#define的区别
- typedef与#define的区别
- typedef和#define的用法与区别
- typedef和#define的用法与区别
- #define typedef 区别
- typedef与#define的区别
- #define和typedef的区别