关键词:
程序的环境和预处理
文章目录
程序的环境一共分为两种:
- 翻译环境:在这个环境中源代码被转换成可执行的机器指令。
- 执行环境:用于实际执行代码。
下图是各个的包含关系:
程序的翻译环境
这里我们重点解释一下翻译环境:
编译
- 以c语言为例,编译是将源文件(后缀为.c)通过编译器转换成目标代码
- 目标文件在通过链接器捆绑在一起,形成一个单一而完整的可执行程序
- 连接器同时也会引入标准c函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,并将其需要的函数也链接入库
预处理(预编译)
- 完成头文件的包含,将库函数里面包含的内容拷贝过来
- 将#define定义的符号和宏进行替换
- 将注释删除
编译
将c语言代码转换成汇编语言
- 语法分析
- 词法分析
- 语义分析
- 符号汇总
汇编
- 生成符号表
- 把汇编代码转化成机器识别的二进制指令
链接
- 合并段表
- 符号表的合并与重定义(跨文件链接函数)
以这个程序为例
这张图简述了符号表在翻译环境中是如何链接不同文件的,链接环节实际上是将有效地址进行保留。
程序的执行环境
- 程序的执行首先调用main函数
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈,存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储于静态内存中的变量在整个程序执行过程中一直保留他们的值
预处理详解
预定义符号
__FILE__ 进行编译的源文件 %s
__LINE__ 文件当前的行号 %d
__DATE__ 文件被编译的日期 %s
__TIME__ 文件被编译的时间 %s
实例:
#include<stdio.h>
#include<stdlib.h>
int main()
FILE* p = fopen("kksk.txt", "w");
fprintf(p, "%s\\t %s", __DATE__, __TIME__);
return 0;
文件写入,文件中就出现了编译的日期和时间
#define
#define定义标识符
语法:
#define NAME staff
举例
#define MAX 100
#define reg register //为register这个关键字,创建一个简短的名字
#define do_foever for(;;) //用更形象的符号来替换循环
#define CASE break;case //再写case语句的时候自动把后面的break补上
但是我以前在书写的时候经常在后面加上;
#define N 100;
int main()
int b = N //等价于
int b = 100;
int c = N + 1;// 这就是错误的了
//等价于 int c = 100; +1;
最主要的是要意识到#define的内容是发生替换,而不是赋值。
#define定义宏
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏
声明方式
#define name(parament-list) stuff
其中parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中,注意:参数列表的左括号必须与name相邻否则就会被认为是stuff的一部分
易错点:
#define fun(a,b) a*b
int main()
printf("%d", fun(1 + 3,2));
return 0;
这里很多人认为答案是8,但实际上答案是7。我们不能以函数的常量表达式来看待参数,而是要以替换的思想去看待,上式其实被转换成了 1+3*2。
#define fun(a,b) (a)*(b)+1
int main()
printf("%d", fun(1 + 3,2)*fun(1,1));
return 0;
这个实际上的计算过程是(1+3)*2+1*(1)*(1)+1
,所以结果为
#define替换规则
- 在调用宏时,首先对参数检查,看是否有宏定义的符号如果有,就进行替换
- 替换文本被插入程序中原来文本的位置。对于宏,参数名被他们替换
带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数的值出现改变(自增自减),那么宏在使用起来会出现不可预料的结果。
例如:
#define fun(a,b) ((a)>(b)?(a):(b))
int main()
int x = 5;
int y = 8;
int z = fun(x++, y++);
printf("%d %d %d",x,y, z);
return 0;
由宏的替换原理我们可以知道:z = ((x++)>(y++)?(x++):(y++));
由于是一个三木操作符所以先判断(x++)>(y++)
;是真是假,由于是后置++所以为假
所以表达式变成 z=(y++)
此时由于自增缘故,x=6,y=9
最后又是后置++所以z=9,x=6,y=10
运行结果:
宏和函数的对比
宏和函数的功能十分相似,为什么不用宏替换函数?
宏相对于函数的优点:
- 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
- 宏的参数是没有类型的,所以可以计算所有类型。而函数的形参是有特定类型的,如果什么的时候是int类型那么传double,float就不行了,而宏就可以很好的完成
宏相对于函数的缺点:
- 每次使用宏的时候,由于宏的方式是替换,所以一大段代码要插入程序中。除非宏比较短,否则可能大幅度增加程序的长度
- 宏是无法调试的
- 宏由于与类型无关,也就不够严谨
- 宏可能会带来运算符优先级的问题,容易出错
- 宏不能递归
宏还有一个函数做不到的就是:宏的参数可以出现类型
#include<stdio.h>
#pragma once
#define Malloc(p,type) malloc(2*sizeof(type))
int main()
int* pf = (int*)Malloc(pf,int);//传入int类型
if (pf == NULL)
perror("malloc");
return 0;
for (int i = 0; i < 2; i++)
pf[i] = i;
free(pf);
pf = NULL;
return 0;
属性 | #define定义的宏 | 函数 |
---|---|---|
代码长度 | 每次使用都会向程序里面插入宏定义的代码段,所以会造成程序大幅度增长 | 函数只出现于一个地方,只有在调用的时候才开辟空间 |
执行速度 | 更快 | 由于函数存在调用和返回,所以相对慢一点 |
操作符优先级 | 宏的操作符要结合上下文,将代码替换到宏出现的位置在进行判断 | 函数传递表达式的时候是先计算出结果在传参 |
参数类型 | 宏没有参数类型,只要是操作合法就可以 | 函数参数类型要严格符合函数定义时的参数类型 |
调试 | 宏无法调试 | 函数可以调试 |
递归 | 宏无法递归 | 函数可以递归 |
命名习惯:
宏:全大写
函数:不要全大写
undef
用于移除一个宏定义。
#undef NAME
如果现存的一个名字需要被重新定义,那么它的旧名字需要首先被移除
条件编译
在编译的过程中我们可以人为的选择性编译,因为我们有条件编译指令
常见的编译指令:
#if 条件
//....
#endif//满足条件编译,不满足条件不编译
#if 条件(常量表达式)
//....
#elif 条件
//......
#endif
不满足编译条件的代码就和注释一样不会参与在预处理阶段被删除
文件包含
- 本地文件包含
#include"filename"
查找策略:现在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置找头文件
- 库文件包含
#inclue<stdio.h>
直接在标准路径下去查找,如果找不到就提示编译错误
嵌套文件包含
如图test.c里面就包含了两个头文件,这样就造成了文件内容重复
解决方法:
- 方法一:
#ifndef __TEST_H_
#define __TEST_H_
#endif
如果头文件是第一次调用,_TEST_H_是没有被定义的,所以进入if里面,进去之后立刻定义__TEST_H,这样下次再调头文件的时候就不会进入头文件了。
- 关键字
#pragma once
解决所有问题
程序环境和预处理(代码片段)
...a6;翻译环境(编译+链接)💦运行环境二、预处理详解💦预定义符号💦#define定义标识符💦#define定义宏💦#define替换规则💦#和##(奇怪的用法)💦带副作用的宏参数 查看详情
程序环境和预处理(代码片段)
程序环境和预处理(二)一、预定义符号介绍预处理详解预定义符号例子:二、预处理指令#define#define#define定义标识符(#define是定义符号的)在编译常出现的几个列子思考:#define定义宏定义语法#define替换... 查看详情
c语言学习笔记(19)程序环境和预处理(代码片段)
...的翻译环境和执行环境翻译环境(编译+链接)预处理编译汇编运行环境预处理预定义符号#define#define定义标识符#define定义宏#define替换规则#和##带副作用的宏参数宏和函数的对比#undef命令行定义条件编译文件包含头文... 查看详情
程序由创建到得到运行结果的过程你知道吗?程序的环境和预处理爆肝总结画图详解(代码片段)
程序的环境和预处理文章目录程序的环境和预处理程序的翻译环境和执行环境编译+链接翻译环境编译也分为三个阶段预处理阶段编译阶段汇编阶段链接阶段运行环境(执行环境)预处理详解预定义符号#define#define定义标识符#defin... 查看详情
c语言进阶学习笔记——程序环境和预处理(代码片段)
...翻译环境和执行环境详解编译+链接翻译环境运行环境预处理详解预定义符号#define#define定义标识符#define定义宏#define替换规则#和##带副作用的宏参数宏和函数宏和函数的对比命名约定#undef命令行定义条件编译文件包含头文件被... 查看详情
程序环境和预处理(代码片段)
...a6;翻译环境(编译+链接)💦运行环境二、预处理详解💦预定义符号💦#define定义标识符💦#define定义宏💦#define替换规则💦#和##(奇怪的用法)💦带副作用的宏参数💦宏和函数的对比... 查看详情
长文详解程序运行是个怎样的环境?预处理阶段在做什么?程序中我们不知道的一些事~(代码片段)
程序环境和预处理老规矩笔记在gitee自取~:程序环境和预处理笔记❤️欢迎喜欢学习C/C++的朋友互关一起努力!!❤️文章目录程序环境和预处理一、程序的环境二、预处理符号三、预处理指令#define1.定义标识... 查看详情
预处理和程序环境(代码片段)
预处理和程序环境一、程序的翻译环境和执行环境二、详解编译+链接三、#define1.#define定义标识符2.#define定义宏3.#define的替换规则4.#和##4.1#的使用4.2##的使用四、宏和函数对比五、条件编译一、程序的翻译环境和执行环境我们... 查看详情
c语言编译过程,满满的干货!!!(代码片段)
程序环境和预处理一、程序翻译和运行环境二、预处理详解1.预定义符号2.define定义宏3.#和##的区别4.宏和函数好坏比较5.命名约定6.头文件中<>和""区别7.条件编译一、程序翻译和运行环境翻译环境:在翻译环境中ÿ... 查看详情
c语言中程序的编译(预处理操作)+链接详解(详细介绍程序预编译过程)(代码片段)
...目录1.前言2.翻译环境和运行环境2.1翻译环境2.2运行环境3.预处理详解3.1预定义符号3.2#define定义的标识符常量和宏3.2.1#define定义的标识符常量3.2.2#define定义的宏3.2.3#define替换规则3.2.4#和##3.2.5带副作用的宏参数3.3宏和函数的对比4.... 查看详情
c语言——程序环境和预处理
程序的翻译环境和执行环境编译+链接预处理一.程序的翻译环境和执行环境在ANSIC标准的任何一种实现中,存在两种不同的环境:翻译环境:该环境中源代码会被转换为可执行的机器指令 执行环境:其用于实际执行代码二.编... 查看详情
c语言-程序环境和预处理(代码片段)
文章目录预处理详解1.预定义符号2.#define2.1#define定义的标识符2.2#define定义宏2.3#define替换规则注意事项:2.4#和###的作用##的作用2.5带副作用的宏参数2.6宏和函数的对比宏的优势:宏的劣势:宏和函数的一个对比命名约... 查看详情
梦开始的地方——c语言预处理+编译过程(代码片段)
文章目录C语言程序的编译(预处理)1.编译和链接1)编译的几个阶段预编译阶段编译阶段汇编阶段2)链接2.预处理1)预定义符号2)#define3)#和##4)带副作用的宏参数5)宏和函数对比3.常见预处理命令1)#undef2)命令行定义3)条件编译4)文件包含5... 查看详情
程序的环境和预处理
1.程序的环境预编译1.头文件的包含2.#define的预处理指令的执行3.注释的删除编译汇编链接运行1.程序的环境写过无数代码的你是否想过你写的.c文件编译链接运行形成.exe文件的中间是怎样执行的吗?从.c文件到.exe文件到代码... 查看详情
c语言篇——程序的编译(代码片段)
...翻译环境和执行环境编译和链接翻译环境编译的几个阶段预处理编译汇编链接运行环境程序的翻译环境和执行环境在ANSIC的任何一种实现中,存在两个不同的环境:第1种是翻译环境,在这个环境中源代码被转换为可执行的... 查看详情
c语言篇——程序的编译(代码片段)
...翻译环境和执行环境编译和链接翻译环境编译的几个阶段预处理编译汇编链接运行环境程序的翻译环境和执行环境在ANSIC的任何一种实现中,存在两个不同的环境:第1种是翻译环境,在这个环境中源代码被转换为可执行的... 查看详情
c语言进阶学习笔记七程序执行+调试技巧(实用技巧篇)(代码片段)
文章目录一、程序执行篇①预处理详解②宏定义③define标识符字符串④define宏名(参数表)字符串⑤宏和函数对比二、调试技巧篇①什么是bug?②调试是什么?有多重要?③debug和release的介绍④windows环境调试介绍⑥如何写出... 查看详情
c语言预处理和程序环境
...译阶段 汇编阶段 链接阶段(不属于编译阶段)预处理详解预定义符号 #define#define定义标识符#define定义宏#define的替换规则 #和##的使用 带副作用的宏参数 宏和函数的对比一些命名的规则 #undef命令行的定义条件编译... 查看详情