当前位置:网站首页>C语言的编译和预处理
C语言的编译和预处理
2022-07-28 05:26:00 【JuLiJuLi.】
1.程序的翻译环境和执行环境
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第2种是执行环境,它用于实际执行代码。
2. 程序如何编译和链接
2.1翻译
1.组成一个程序的每个源文件通过编译过程分别转换成目标代码。
2.每个目标文件由链接器连接在一起,从而形成一个单一而完整的可执行程序。
3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数,它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
2.2编译
编译阶段会有以下几个阶段:
2.3.执行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。 - 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈,存储函数的局部变量和返回地址。
程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。 - 终止程序。正常终止main函数;也有可能是意外终止。
3.预处理
3.1预处理符号有哪些
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
3.2#define
3.2.1#define定义的标识符
示例:
#define MAX 1000
3.2.2#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏.
注意:
参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分,可能导致程序结果错误.
注意下面这段代码:
#define SQUARE( x ) x * x
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
如果我们像这样定义宏 结果会跟我们想要的结果差别很大,因为宏是直接把参数替换到代码中去的,上面的代码可以转换为
#define SQUARE( x ) 5+1 * 5+1 结果是11,
所以我们尽量使用#define SQUARE( x) ( ( x ) + ( x ) )这样来定义。
所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
3.2.3#define 替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤:
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意: - 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索.
4.#的作用
#的作用是:使用 #号 ,可以把一个宏参数变成对应的字符串。
#include<stdio.h>
int main()
{
int a=10;
printf("the value of a is %d\n",a);
int b=20;
printf("the value of b is %d\n",b);
return 0;
}
先来看上面这段代码,可以看出上面的代码相似的地方很多,能不能用一个宏来代替这段打印代码呢?并且输出的是 the value of n is n呢?让n的值随着宏参数而改变呢?
#include<stdio.h>
#define PRINT(N) printf("the value of" #N "is "%d\n",N)
int main()
{
int a=10;
PRINT(a);
int b=20;
PRINT(b);
return 0;
}
使用 # ,可以把一个宏参数变成对应的字符串,就可以使参数N不进行替换,直接输出传过来的n,最终达到我们要的效果。
5.##的作用
##的作用是可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。
注意:这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。
示例:
#include<stdio.h>
#define CAT(name,num) name##sum
int main()
{
int class105=105;
printf("%d\n",CAT(class,105));
return 0;
}
这段代码输出的结果就是105.
6.函数和宏的命名约定
一般来讲函数的宏的使用语法很相似,所以语言本身没法帮我们区分二者,我们平时尽量的一个习惯是:
把宏名全部大写
函数名不要全部大写
7.#undef
#undef的作用是移除一个宏定义
举例:
#include<stdio.h>
#define CAT(name,num) name##sum
int main()
{
int class105=105;
printf("%d\n",CAT(class,105));
#undef CAT //下面再使用CAT这个宏时编译器会报错,错误显示CAT未定义
return 0;
}
8.条件编译指令
#include<stdio.h>
int main()
{
int i=0;
int arr[10]={
0};
for(i=0;i<10;i++)
{
arr[i]=i;
#if 1 //如果表达式的结果为真,执行#denif前面的代码,为假不执行
printf("%d ",arr[i]);
#endif //用来结束#if的,这两条语句是配合使用的
}
return 0;
}
常见的一些条件编译指令
1.
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
2.多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
9.文件包含
我们知道, #include 指令可以使另外一个文件被编译,编译器在预处理器时会先删除这条指令,并用包含文件的内容替换,如果这样一个源文件被包含10次,那就实际被编译10次。
9.1头文件被包含的方式

编译器会先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。
如果找不到就提示编译错误。
库文件包含的时候,编译器查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
我们可以对于库文件也可以使用" "的形式包含,但是不建议这样做,这样做查找的效率就低些,也不容易区分是库文件还是本地文件。
边栏推荐
猜你喜欢

气传导耳机哪个品牌比较好、这四款不要错过

MySQL安装与使用

小程序:生命周期

Matlab simulation of radar imaging 1 - LFM signal and its spectrum

Combine multiple ICs calendars into a single ICs calendar

开放式耳机推荐哪款最好、性价比最高的开放式耳机

到底什么是Hash?(量化交易机器人系统开发)

Fluke dtx-sfm2 single mode module of a company in Hangzhou - repair case

The startup fails when switching Chinese when using wampserver3.2.6

【无标题】
随机推荐
Perl introductory learning (VIII) subroutine
Find the network address and broadcast address of the host according to the IP address and subnet mask
2022-06-07 六.日志实现
Listener
Servlet
2022-07-19 达梦数据库 连接实例、执行脚本、系统命令
Pyppeteer 被识别 绕过检测
execjs 调用
【学习笔记】线程创建
2022-07-17 达梦数据库安装
气传导蓝牙耳机哪个好、气传导蓝牙耳机排行榜
开放式耳机有哪些、四款音质超好的气传导耳机推荐
suger BI 创建任务
一、ffmpeg录制音频为pcm文件
QT implementation outputs relevant information to log file
QT painting event - set background picture
MySQL delete tables without deleting databases
Pytorch learning notes 1 - quick start
七夕礼物送女生什么好?颜值在线又有心意的礼物推荐
How many columns are the most suitable for Clickhouse to build a width table?