目录
1.编译环境和运行环境
翻译环境:在这种环境中,源代码被替换成可执行程序;
运行环境:在这种环境中,可执行程序被执行,最终产生运行结果。
如下图:
- 编译环境:
预编译过程:实现头文件的包含、注释的删除、define 符号的替换;
编译:语法分析、词法分析、语义分析、符号汇总、将C语言的代码转换成汇编代码;
汇编:将汇编指令转换为二进制指令、形成符号表;
链接:合并段表、符号表的合并和重定位。
- 运行环境:
将程序导入内存,程序便开始执行,接着便调用main函数,执行代码,终止程序。
2.预处理
#define
-
#define定义标识符
直接替换
#define MAX 9197
#define STR "bjyx"
int main()
{
printf("%d\n", MAX);//9197
printf("%s\n", STR);//bjyx
return 0;
}
注意:define定义标识符的时候,最后不要加上;
-
#define定义宏
#define MAX(x,y) x>y?x:y
int main()
{
printf("%d\n", MAX(2,3));
return 0;
}
注意:定义的宏name必须与左边列表的括号紧密相连
下面我们来看一段代码:
#define SQUARE(x) x*x
int main()
{
printf("%d\n", SQUARE(3 + 1));
return 0;
}
//那么此时的结果还是16吗?
答案是否定的,经过编译我们发现答案是7,原因:define定义宏也是进行直接替换的 ,3+1*3+1=7。
经过修改:
#define SQUARE(x) (x)*(x)
int main()
{
printf("%d\n", SQUARE(3 + 1));
return 0;
}
我们再来看一组代码:
#define DOUBLE(x) (x)+(x)
int main()
{
printf("%d\n", 10*DOUBLE(3));//60?
return 0;
}
经过编译,发现答案是33,这是因为乘法的优先级先于宏定义的加法。10*(3)+(3)=33。
修改:
#define DOUBLE(x) ((x)+(x))
int main()
{
printf("%d\n", 10*DOUBLE(3));//33
return 0;
}
所以,在宏定义数值运算的时候,最好都应该用上述方式加上括号。
-
#define替换规则
直接替换!
1、在调用宏时,首先检查参数,如果参数包含#define定义的符号,首先被替换;
2、替换文本,被插入程序中文本原来的位置,对于宏,参数名被他们原来的值所替换;
3、对结果文件进行扫描,如果包含#define定义的符号,则进行上述步骤。
扩展:
1、宏参数和#define定义中可以出现其他#define定义的符号,但是不能进行递归;
2、预处理检查#define定义的符号时,字符串不会被搜索
#define MAX 10; int main(){ char str[]="hahaMAX";//不会被替换 return 0; }
#和##
#可以把一个宏参数变成对应的字符串
首先,我们看一下下述代码:
printf("bjyx""happy""\n");//biyxhappy
//字符串具有自动连接的功能
int main()
{
int a = 1;
int b = 2;
double c = 3.0;
printf("the value of a is %d\n", a);
printf("the value of b is %d\n", b);
printf("the value of c is %d\n", c);
//上述代码可以通过函数来实现吗?---显而易见,不可以!
return 0;
}
我们能否用宏来实现呢?此时,我们就需要运用#了。
#define PRINT(number,format) printf("the value of "#number" is " format "\n",number)
// #number相当于“a”/"b"/"c"...
int main()
{
int a = 1;
int b = 2;
double c = 3.0;
PRINT(a, "%d");
PRINT(b, "%d");
PRINT(c, "%lf");
return 0;
}
##可以把位于它两边的符号合并成一个符号
#define COMBINE(a,b) a##b
int main()
{
int love = 1823;
printf("%d\n", COMBINE(lo, ve));//1823
return 0;
}
注意:它的连接必须产生一个合法的标识符,否则结果是未定义的。
带副作用的宏参数
如果宏参数带有副作用,就会给程序带来不可预测的危险。
例如:
#define MAX(x,y) ((x)>(y)?(x):(y))
int main(){
int a=1;
int b=2;
printf("%d\n", MAX(a++, b++));//3
printf("%d\n", a);//2
printf("%d\n", b);//4
return 0;
}
不仅返回值改变,a,b的值都改变了。
#undef
用语移除一个宏指令。
条件编译
由于有条件编译,是的我们在编译一个程序是将一组(条)语句编译或者放弃是很方便的。
下面,我们来简单介绍一下条件编译:
常见的条件编译指令:
#if 常量表达式(由预处理求值)
#endif
例如:#define MAX 1
#if MAX
#endif
多个分支的条件编译
#if 常量表达式
#elif 常量表达式
#else 常量表达式
#endif
判断是否被定义
#if defined(symbol)
#ifdef symbol
(如果被定义了怎么怎么样)
#if !defined(symbol)
#ifndef symbol
(如果没有被定义)
嵌套指令
#if defined()
#ifdef()
#endif
#elif defined()
#ifdef()
#endif()
#endif
总的来说,类似于我们之前学的if ...else语句。
文件包含
#include<stdio.h>
#include"game.h"
上述都属于文件包含,那为什么有的使用<>,而有的是用“”呢?
<>从库文件中寻找(从编译选项指定的库目录中寻找)
“”从当前编译的源文件所在目录中去寻找,找不到就从库文件中寻找。
嵌套文件包含:
嵌套文件包含是指一个文件包含另外一个文件,同时被包含的文件又包含了另外一个文件。
如果头文件嵌套的话,会造成包含很多不必要的头文件。
1、使用条件编译:
ifndef/defined /endif
2、另外,还可以使用:
#pragma once
都可以避免头文件的重复引入。
总结
祝大家顺颂时宜,百事从欢。