1.define
define宏展开是在预编译过程进行替换。
#include<stdio.h> #include<typeinfo> using namespace std; //C++ #define int int * //注意不能误加分号 void main() { int a,b; //预编译:int *a,b; 【*和变量名结合,不和类型名结合。】 a为指针,b为整型。 printf("%s \n",typeid(a).name()); //typeid是运算符(不是函数),检查表达式的类型。 printf("%s \n",typeid(b).name()); //头文件 <typeinfo>里定义,属于C++语法。 .name() 返回类型名字。 #undef int //取消宏替换。 int p; int s; }
运行结果: int *
int
也许你会想,上面代码用关键字做宏替换是怎么编译通过的?
那是因为为宏展开是在预编译阶段进行替换,已经替换了,编译阶段才进行类型检查,所以不要紧。
#include<stdio.h>#define MAX(a,b)((a)>(b)?(a):(b)) int Max(int a,int b){return a>b?a:b;} //那么,分别用宏展开和函数调用,实现找出两数中较大值。二者区别在哪? //一个是替换 一个是函数调用 void main() { int x=10,y=10; MAX(++x,y); //((++x)>(y)?(++x):(y)) printf("%d\n",x); }
宏具有副作用 使用宏,主函数中x=12。而调用函数,x=11;
宏在预编译阶段替换,不做类型检查,具有某种程度上的泛型;
宏的运行速度比函数的速度要快。 函数调用进行现场的保护和恢复。
再看看宏定义中的##和#
#include<stdio.h> #define CHARSTR(exp) char zyz##exp[10]=#exp void main() { CHARSTR(zyz); //char zyzzyz[10]="zyz"; CHARSTR(hm); //char zyzhm[10]="hm"; } //##链接两个成一个新的名称 #变成一个字符串 //预编译阶段完成,不检查类型。(编译阶段是要检查类型的)
2.typedef
typedef类型重命名是在编译时处理的。
#define PINT int * typedef int * SINT //类型重命名 (注意区分类型和变量)
void main()
{
PINT a,b;
SINT x,y;
}
类型——图纸
变量——根据图纸盖的房子 (只有变量可以分配内存空间)
重命名的好处/必要性?
比如我们在写 int *p,*t,*s; 时,容易丢失星号误写成 int *p,*t,s;
这时候typedef int * SINT之后,直接写 SINT p,t,s; 就可以了。
3.编译与执行四阶段
顺带,查了一下编译与执行的四个阶段是怎么回事。
第一个阶段是预编译阶段,在正式的编译阶段之前进行。预处编译段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。宏替换也是在预编译阶段完成的。在预编译完成后,也即.i文件中,所有以#开头的语句都不存在,已经被替换了。
第二个阶段编译、优化阶段,将其翻译成等价的中间代码表示或汇编代码,并执行优化。
第四个阶段是链接。