BIT-0程序环境和预编译 c语言预处理

1.程序的翻译环境
2.程序的执行环境
3.c语言程序的编译+链接
4.预定义符号介绍
5.预处理指令#define
6.宏和函数的对比
7.预处理操作符#和##的介绍
8.命令定义
9.预处理指令#include
10.预处理指令#undef
11.条件编译

程序的翻译环境和执行环境

在ANSI C的任何一种实现中,存在两个不同的环境,第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令,第二种是执行环境它用于实际执行代码

在这里插入图片描述

详解编译+链接
翻译环境

程序的编译过程

1.组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)
2.每个目标文件由连接器(linker)捆绑在一起,形成一个单一而完整的可执行程序
3.连接器同时也会引入标准c函数库中任何被该程序所用到的函数,而且它还可以搜索程序员个人的程序库,将其需要的函数也链接到程序中

通俗讲上面这段话就是 首先翻译环境分为两个阶段,一个叫编译 ,一个叫链接,编译会依赖编译器 编译完了会生成目标文件 目标文件再经过连接器进行链接最终处理生成可执行程序
在这里插入图片描述

编译又可以分为几个阶段

在这里插入图片描述

第一个阶段 预编译

做的事情是
1. #include头文件的包含,把头文件的内容全部都包含放到.c文件里面去
2.删除注释,使用空格来替换注释
3.将#define定义的符号 全部换成所对应的值
总归一句话预处理做的事情叫做文本操作
test.c文件经过编译器处理会生成一个test.i的文件

第二个阶段 编译

做的事情是
编译是把test.i翻译成test.s把c语言代码翻译成了汇编代码
1.语法分析,看语法有没有错误
2.词法分析
3.语义分析
4.符号汇总 比如全局变量 函数 main g_val
其实就是把c语言代码转换为汇编代码

第三个阶段 汇编

将汇编代码转换生成test.o文件 test.o在windows下是test.obj文件也就是目标文件
做的事情是
1.把汇编代码转换成二进制的代码,也称二进制指令 指令就是代码
2.上一个阶段不是进行符号汇总了,在汇编阶段形成符号表
符号表就是各自的文件中的符号 比如函数名 和全局变量 和它们的地址的组成

那么如何形成符号表
全局变量和函数名
#include<stdio.h>
extern int Add(int x,int y)
int main()
{
   
	return 0;
}
编译阶段不是已经产生符号汇总了
所以汇编阶段就会形成符号表
这里面会产生符号是全局变量ADD和main函数
符号汇总会形成
ADD 0x112233
main 0x223344
将符号和它的地址对应起来就会形成符号表
汇编完了之后生成test.0文件 然后通过连接器生成可执行文件

1.预处理选项 gcc-E test.c -0 test.i 预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中
2.编译 选项 gcc-S test.c 编译完成之后就停下来,结果保存在test.s中
3.汇编 gcc-c test.c汇编完成之后就停下来,结果保存在test.o中 .o文件在windows下就是.obj文件

连接发生的事情

1.合并段表
首先目标文件也就是.obj文件它会有自己的格式 会自己分成几个段
将多个目标文件连接在一起 对应段的数据合并在一起,这就是合并段表最终生成一个可执行程序
2.符号表的合并和符号表的重定位
将各自目标文件的符号表合并成为一个符号表
//符号表合并如果符号名相同肯定会用地址有效的符号表
整个翻译环境所做的事情完成 生成可执行程序

在这里插入图片描述
在这里插入图片描述

运行环境

程序执行的过程
1.程序必须载入内存中,在有操作系统的环境中:一般这个由操作系统完成,在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成
2.程序的执行便开始,接着便调用main函数
3.开始执行程序代码,这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值
4.终止程序,正常终止main函数,也有可能是意外终止

预编译阶段也叫预处理阶段

预处理详解

预定义符号

  1. FILE 文件名符号
  2. LINE 行号
  3. DATE 获取日期
  4. TIME 获取时间
  5. STDC 果编译器遵循ANSI C 其值为1 否则未定义
    这些预定义符号都是语言内置的
int main()
{
   
	printf("%s\n", __FILE__);//文件名符号   d:\桌面\c\date11_2\test11_2\test11_2\today.c
	printf("%d\n", __LINE__);//行号    101查看 你在多少行
	printf("%s\n", __DATE__);//获取日期	 Nov  2 2021
	printf("%s\n", __TIME__);//获取时间	 17:39:36
	printf("%s\n", __STDC__);//如果编译器遵循ANSI C 其值为1 否则未定义
	//写日志文件
	int i = 0;
	int arr[10] = {
    0 };
	FILE* pf = fopen("log.txt", "w");
	for (i = 0; i < 10; i++)
	{
   
		arr[i] = i;
		fprintf(pf, "file:%s line:%d data:%s time:%s i=%d\n", 
			__FILE__,__LINE__,__DATE__,__TIME__,i);
	}
	fclose(pf);
	pf = NULL;
	for (i = 0; i < 10; i++)
	{
   
		printf("%d ", arr[i]);
	}
	return 0;
}

预处理指令都有哪些

#define
#include
#if
#end
#end if
#pragma pack(4)
#pragma
#ifdef
#line
以#开头的都叫预处理指令

#define 预处理指令

#define 定义标识符

语法 #define name stuff
实例

	#define MAX 1000
	#define reg register //为register这个关键字,创建一个简短的名字
	#define do_forever for(;;)//用更形象的符号来替换一种实现
	#define CASE break;case//再写case语句的时候自动把break写上

	//如果定义的stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续航符)
	#define DEBUG_PRINT print("file:%s\t line:%d\t	  \
								date:%s\t time:%s\n", \
								__FILE__,__LINE__,    \
								__DATE__,__TIME__)

结论 #define max 1000 后面不要加; 因为容易导致语法错误

define 定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)

下面是宏的申明方式:

#define name(parament-list)stuff 其中的parament-list是一个由逗号隔开的符号表他们可能出现在stuff中
注意:参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在参数列表就会被解释为stuff的一部分

#define SQUARE(X) ((X)*(X)) //SQUARE就是宏

#define SQUARE(X) ((X)*(X))
这里将5给x 然后后面的替换掉x就是5x5了
牢牢记住这里不是把5传给x而是把5跟x进行了替换,也就是写宏的时候永远
不要忘记括号

int main()
{
   
	int ret = SQUARE(5);
	printf("%d\n"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值