深入学习C语言系列(二): #define与typedef
2010-11-15 13:26
323 查看
#define与typedef
先举个例子:
#define PI 3.1415926;
复制代码
上面这行代码的意思是:将3.1415926用PI来代替,后面的代码中凡是要用到3.1415926的地方皆可写作PI。
值得注意的是,define语句是被编译器在“预编译”阶段进行“替换”处理的,也就是说,当我们生成可执行文件的时候,可执行文件里并没有PI这个内容,而统统是3.1415926。
于是#define的作用就是在编程的时候为代码编写提高便利,将“魔数”(纯数字)用一个符号(例如本例中的PI)来代替。这样的好处就是减少了书写工作量,不易出错,并且易于统一修改。
再看一个typedef的例子:
typedef char int8;
复制代码
这个例子的意思是:给关键字char起个“别名”叫做int8。所谓别名,也就是外号的意思。
这样我们就知道了,在后面的代码中,任何一个要使用char的地方,都可以使用int8来代替。
typefdef的主要作用是使得一些比较长的关键字可以用一个较短的“外号”来替代,例如:
typedef unsigned long int uint32;
复制代码
上面这行代码就把unsigned long int给起了个别名,叫做uint32,是不是简洁了许多呢?!这样如果我们要声明一个变量
unsigned long int XX;
复制代码
的话,就只需写为uing32 XX就可以了。
至此,我们已经介绍完了#defind和typedef的作用,很多人认为他们俩作用类似,分不清有什么区别。现在我们来区分一下他们:
1.编译时间不同:#define是在预编译阶段编译的,而typedef是在编译的时候才执行的。
2.#define只是简单的替换,而typedef是为一个原有的关键字起一个“别名”。
①#define经常被用来处理“魔数”问题,例如
#define PI 3.1415926;
复制代码
,而typedef不可以,因为“别名”也是名,要符合命名规则。
②typedef的一个重要的用武之地是给结构体起别名,例如:
struct student
{
char name[16];
int age;
double score;
};
复制代码
可以用typedef处理为:
typedef struct student
{
char name[16];
int age;
double score;
}Stu,*pStu;
复制代码
这样的好处是,以后凡是声明变量时用到形如“
struct student XX;
复制代码
”的地方,都可以简写为
Stu XX;
复制代码
凡是声明变量时用到形如“
struct student *p;
复制代码
”的地方,都可以简写为
pStu p;
复制代码
。
有人可能会问,用
#define struct student Stu;
复制代码
和
#define struct student *pStu;
复制代码
不一样可以实现简化功能吗?实际上,不完全是这样的。我们看下面的代码:
struct student
{
char name[16];
int age;
double score;
};
#define struct student *pStu_1;
typedef struct student *pStu_2;
复制代码
这两行代码都实现了“简化代码”的作用,但是简化的效果是不一样的。如果我们写这样一句代码:
pStu_1 A,B,C;
复制代码
那么,只有A是指针变量,B和C都是struct student类型的变量,因为这句代码“反替换”后原型是:
struct student *A,B,C;
复制代码
跟
int X,Y,*Z
复制代码
类似。
而如果我们改用
pStu_2 A,B,C;
复制代码
来声明变量,其结果是A,B,C都是struct student* 类型的指针变量,因为“别名”是“全功能”的。上面这行代码的原型是:
struct student *A,*B,*C;
复制代码
3.数组的问题:
如果我们写一行这样的代码:
typedef int line[16];
复制代码
那么后面如果我们这样来声明一个变量:
line a;
复制代码
那么a就是一个具有16个sizeof(int)大小的变量(或者说内存区域)。因为它的原型是:
int a[16];
复制代码
这其实就是声明了一个数组,a是数组名,亦即该片内存的首地址。这种使用方法常见于声明一个大小固定且连续的内存做“数据缓冲区”的时候。
4.参数问题:
#define是可以带参数的,例如:
#define XX(a,b) myfun(a,b);
复制代码
其中myfun(a,b)是一个函数调用,a,b是传给myfun()函数的参数,以后皆可以用宏定义XX(a,b)来代替这个函数调用了,而且在宏定义里参数(a,b)可以像变量一样为合理的任意值。
有人可能要问:我直接在代码里写myfun(a,b)来调用子函数不就可以了,干嘛要多此一举搞个宏定义呢?答案很简单,因为宏定义不仅能使你的代码变简短,而且它的参数类型是任意的(或者说参数没有类型),这在某些时候很有用!
先举个例子:
#define PI 3.1415926;
复制代码
上面这行代码的意思是:将3.1415926用PI来代替,后面的代码中凡是要用到3.1415926的地方皆可写作PI。
值得注意的是,define语句是被编译器在“预编译”阶段进行“替换”处理的,也就是说,当我们生成可执行文件的时候,可执行文件里并没有PI这个内容,而统统是3.1415926。
于是#define的作用就是在编程的时候为代码编写提高便利,将“魔数”(纯数字)用一个符号(例如本例中的PI)来代替。这样的好处就是减少了书写工作量,不易出错,并且易于统一修改。
再看一个typedef的例子:
typedef char int8;
复制代码
这个例子的意思是:给关键字char起个“别名”叫做int8。所谓别名,也就是外号的意思。
这样我们就知道了,在后面的代码中,任何一个要使用char的地方,都可以使用int8来代替。
typefdef的主要作用是使得一些比较长的关键字可以用一个较短的“外号”来替代,例如:
typedef unsigned long int uint32;
复制代码
上面这行代码就把unsigned long int给起了个别名,叫做uint32,是不是简洁了许多呢?!这样如果我们要声明一个变量
unsigned long int XX;
复制代码
的话,就只需写为uing32 XX就可以了。
至此,我们已经介绍完了#defind和typedef的作用,很多人认为他们俩作用类似,分不清有什么区别。现在我们来区分一下他们:
1.编译时间不同:#define是在预编译阶段编译的,而typedef是在编译的时候才执行的。
2.#define只是简单的替换,而typedef是为一个原有的关键字起一个“别名”。
①#define经常被用来处理“魔数”问题,例如
#define PI 3.1415926;
复制代码
,而typedef不可以,因为“别名”也是名,要符合命名规则。
②typedef的一个重要的用武之地是给结构体起别名,例如:
struct student
{
char name[16];
int age;
double score;
};
复制代码
可以用typedef处理为:
typedef struct student
{
char name[16];
int age;
double score;
}Stu,*pStu;
复制代码
这样的好处是,以后凡是声明变量时用到形如“
struct student XX;
复制代码
”的地方,都可以简写为
Stu XX;
复制代码
凡是声明变量时用到形如“
struct student *p;
复制代码
”的地方,都可以简写为
pStu p;
复制代码
。
有人可能会问,用
#define struct student Stu;
复制代码
和
#define struct student *pStu;
复制代码
不一样可以实现简化功能吗?实际上,不完全是这样的。我们看下面的代码:
struct student
{
char name[16];
int age;
double score;
};
#define struct student *pStu_1;
typedef struct student *pStu_2;
复制代码
这两行代码都实现了“简化代码”的作用,但是简化的效果是不一样的。如果我们写这样一句代码:
pStu_1 A,B,C;
复制代码
那么,只有A是指针变量,B和C都是struct student类型的变量,因为这句代码“反替换”后原型是:
struct student *A,B,C;
复制代码
跟
int X,Y,*Z
复制代码
类似。
而如果我们改用
pStu_2 A,B,C;
复制代码
来声明变量,其结果是A,B,C都是struct student* 类型的指针变量,因为“别名”是“全功能”的。上面这行代码的原型是:
struct student *A,*B,*C;
复制代码
3.数组的问题:
如果我们写一行这样的代码:
typedef int line[16];
复制代码
那么后面如果我们这样来声明一个变量:
line a;
复制代码
那么a就是一个具有16个sizeof(int)大小的变量(或者说内存区域)。因为它的原型是:
int a[16];
复制代码
这其实就是声明了一个数组,a是数组名,亦即该片内存的首地址。这种使用方法常见于声明一个大小固定且连续的内存做“数据缓冲区”的时候。
4.参数问题:
#define是可以带参数的,例如:
#define XX(a,b) myfun(a,b);
复制代码
其中myfun(a,b)是一个函数调用,a,b是传给myfun()函数的参数,以后皆可以用宏定义XX(a,b)来代替这个函数调用了,而且在宏定义里参数(a,b)可以像变量一样为合理的任意值。
有人可能要问:我直接在代码里写myfun(a,b)来调用子函数不就可以了,干嘛要多此一举搞个宏定义呢?答案很简单,因为宏定义不仅能使你的代码变简短,而且它的参数类型是任意的(或者说参数没有类型),这在某些时候很有用!
相关文章推荐
- 深入学习C语言系列(二): #define与typedef
- C语言深入学习系列 - 字节对齐&内存管理
- c语言深入学习系列内存管理
- C语言深入学习系列 - 字节对齐&内存管理
- C语言深入学习系列 - 字节对齐&内存管理
- 深入学习C语言系列(一):setjmp( )详解
- C语言深入学习系列 - 字节对齐&内存管理
- C语言深入学习系列 - 字节对齐&内存管理(怎么会有这么好的资料呢 兴奋。。。。)
- C语言深入学习系列 - 字节对齐&内存管理
- C语言深入学习系列+-+字节对齐and内存管理
- C语言深入学习系列 - 字节对齐&内存管理
- C语言深入学习系列 - 字节对齐&内存管理
- C语言深入学习系列
- 【PSI/SI学习系列】2.PSI/SI深入学习3——SI信息解析2(SDT, EIT, TDT,TOT)——神文004
- [转]深入C#学习系列一:序列化(Serialize)、反序列化(Deserialize)
- 深入Java集合学习系列:HashMap的实现原理
- 深入C#学习系列二:不可小瞧的using关键字
- Java8深入学习系列(一)lambda表达式介绍
- 深入学习Web Service系列----异步开发模式
- undo系列学习之深入剖析一个事务的操作流程