一、翻译环境和执行环境
翻译环境:在这个环境中源代码被转换为可执行的机器指令
执行环境:用于实际执行代码
具体什么意思我们通过画图来解释
1.翻译环境
翻译环境分为两步,编译和链接
那么编译和链接是什么呢,同样以图来说明
那么问题又来了,编译和链接的过程中发生了什么
由于编译和链接内部需要Linux环境深入了解,这里直接上定义
2.执行环境
简述一下程序执行的过程
1.程序必须载入内存中.
在有操作系统的环境中:一般由操作系统完成
在独立的环境中:程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存完成
2.程序执行便开始,调用main函数
3.开始执行程序代码.这个时候程序将使用一个运行的堆栈(stack)(堆栈就是栈),存储函数的局部变量和返回地址.
程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值.
4.终止程序.正常终止main函数,也可能是意外终止.
以上
二、预处理详解
1.预定义符号
__FILE__ //进行编译的源文件 %s
__LINE__ //文件当前的行号 %d
__DATE__ //文件被编译的日期 %s
__TIME__ //文件被编译的时间 %s
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义 %d
2.#define定义宏
#define允许把参数替换到文本中
勤加()
3.#和##
①#
#可以把参数替换到字符串中
引出一下常见情况
void print(int x)
{
printf("??的结果是%d\n",x); // ?? 里面到底放什么呢
//显然函数并不能做到这个功能
}
int main()
{
int a = 10;
//打印出: a的结果是10
print(a);
int b = 20;
//打印出: b的结果是20
print(b);
int c = 30;
//打印出: c的结果是30
print(c);
}
所以我们使用宏
首先我们要知道
printf("halo");
printf("ha""lo");
//这两个代码的结果是一样的,由此
#define print(X) printf(#X "的结果是%d",X)
int main()
{
int a = 10;
//打印出: a的结果是10
print(a);
//本质上来讲,替换后
printf(#X "的结果是%d",X);
printf("a" "的结果是%d",X);
int b = 20;
//打印出: b的结果是20
print(b);
int c = 30;
//打印出: c的结果是30
print(c);
}
有
//格式不同的处理
#define print(X,FORMATE) printf(#X "的结果是"FORMATE,X)
int main()
{
int a = 20;
//打印出: a的结果是20
print(a, "%d\n");
float b = 5.5;
//打印出: b的结果是5.5
print(b, "%f\n");
}
注意:#不能直接用在printf中,只能在宏内使用
②##
直接举个例子
#define CAT(X,Y) X##Y##Z
int main()
{
int class1011 = 100;
printf("%d\n",CAT(class,10,11));
printf("%d\n",class1011);
//两者打印结果相同,可以将##看作是一个连接符号
return 0;
}
4.宏和函数的取舍
属性 | #define定义宏 | 函数 |
---|---|---|
代码长度 | 每次使用时,宏代码都会被插入程序中.除了非常小的宏除外,程序的长度会大幅提升 | 函数代码只出现于一个地方,每次调用这个函数时,都调用那个地方的同一份代码 |
执行速度 | 更快 | 存在函数的调用和返回的额外开销,所以相对较慢 |
操作符优先级 | 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写时多些括号 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数.表达式的求值结果更容易预测 |
带有副作用的参数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果 | 函数传参只在传参的时候求值一次,结果更容易控制 |
参数类型 | 宏的参数与类型无关,只要对参数的操作时合法的,它就可以使用于任何参数类型 | 函数的参数与类型有关,如果参数的类型不同,就需要不同的函数 |
调试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递归 | 宏不能递归 | 函数可以递归 |
以上
5.undef
该指令可以移除一条宏定义
比如说
#undef NAME
6.条件编译
先上一个例子
#define ADD
int main()
{
#ifdef ADD //如果定义了ADD宏,那么endif前就可以运行,反之则会在预处理时被略过
printf("666");
#endif
#if 1 //非零则进入,否则在预处理时被略过
printf("666");
#endif
return 0;
}
#ifdef 后跟宏名称
#if 后跟常量表达式
同样都是以#endif作为结尾
同样的还有#else 、#elif
其后面跟的都是常量表达式
int main()
{
#if 1==1 //常量表达式
printf("666");
#elif 2==2 //常量表达式
printf("999");
#else 3==3 //常量表达式
printf("114514");
#endif
//同if else 一样,这三条语句只能进入一个,不能同时进入
return 0;
}
也有
#ifndef 作用与 #ifdef相反
#if defined() 括号内加入宏名,作用与 #ifdef相同
也可以#if !defined(),作用与 #ifdef相反