您的位置:首页 > 其它

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: