程序环境和预处理(上)

简介: 程序环境和预处理(上)

重点:程序的翻译环境、程序的执行环境、详解:C语言程序的编译+链接、预定义符号介绍 、预处理指令 #define 、宏和函数的对比 、预处理操作符#和##的介绍 、命令定义 、预处理指令 #include 、预处理指令 #undef 、条件编译


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

不同的编译器,对于缓冲区实现的方式不同。

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

二 详解编译+链接

2.1 翻译环境

翻译环境分成两个部分:编译和链接;(C语言源代码通过编译生成目标文件(.obj/.o)(多个源文件单独通过编译器生成各自的目标文件),目标文件加上链接库通过链接器 链接成为可执行程序(.exe))

编译过程:预编译、编译、汇编

2.2 编译本身也分为几个阶段

预编译(预处理)——编译——汇编  (预编译也叫作预处理)

     预处理之后产生的结果都放在test.i文件中,编译完成之后产生的结果保存在test.s中,汇编完成之后产生的结果保存在test.o或者是test.obj(在linux中是.o;Windows中是.obj)。

预编译:(1)进行头文件的展开(2)删除注释(3)#define定义的符号替换(例如:#define Max 100,在预编译结束之后,Max会被替换为100,并把#define这一行给删掉)

总而言之,就是进行一些文本操作。

编译(把C语言代码转换成汇编代码): (1)语法分析(2)词法分析(3)语义分析(4)符号汇总(全局符号:例如:函数名、main等)

汇编(把汇编代码转换成二进制的指令):(1)形成符号表(全局符号给一个地址,所有全局符号+地址形成符号表)

目标文件(.o)加上链接库通过链接器 链接成为可执行程序(.exe))

linux中.o目标文件以及可执行文件的文件格式是elf

链接(编译之后生成.o文件,加上链接库通过链接器进行链接):(1)合并段表(2)符号表(不同文件的符号表)的合并以及重定义

链接的时候,多个目标文件进行链接的时候会通过符号表,查看来自外部的符号是否真实存在

2.3 运行环境

程序执行的过程:

1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。

2. 程序的执行便开始。接着便调用 main 函数。

3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack ),存储函数的局部变量和返回 地址。程序同时也可以使用静态( static )内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值。

4. 终止程序。正常终止 main 函数;也有可能是意外终止。

三 预处理详解

这里讲述的内容都是在预处理阶段

3.1 预定义符号

__FILE__       // 进行编译的源文件

__LINE__     // 文件当前的行号

__DATE__     // 文件被编译的日期

__TIME__     // 文件被编译的时间

__STDC__     // 如果编译器遵循 ANSI C ,其值为 1 ,否则未定义 (这个并不是所有的编译器都支持)

例子:

1. #include <stdio.h>
2. int main()
3. {
4.  printf("%s\n", __FILE__);//编译出来的结果,会显示运行文件的路径
5.  printf("%d\n", __LINE__);//编译出来的结果,显示该行的行数303
6.  printf("%s\n", __DATE__);//显示时间
7.  printf("%s\n", __TIME__);//显示日期
8.  return 0;
9. }

记录杂志:

1. #include <stdio.h>
2. #include <string.h>
3. #include <errno.h>
4. int main()
5. {
6.  int i = 0;
7.  FILE* pf = fopen("log.txt", "w");
8.  if (pf == NULL)
9.  {
10.     printf("%s\n", strerror(errno));
11.     return 0;
12.   }
13.   for (i = 0; i < 10; i++)
14.   {
15.     fprintf(pf, "%s %s %s %d %d\n", __DATE__, __TIME__, __FILE__, __LINE__, i);
16.   }
17.   fclose(pf);
18.   pf = NULL;
19.   return 0;
20. }

3.2 #define

3.2.1 #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 printf("file:%s\tline:%d\t \

                                              date:%s\ttime:%s\n" ,\

                                              __FILE__,__LINE__ ,       \

                                               __DATE__,__TIME__ )

3.2.2 #define定义宏

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

#define name( parament - list ) stuff

其中的 parament - list 是一个由逗号隔开的符号,它们可能出现在 stuff 中。

注意: 参数列表的左括号必须与name 紧邻。 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分

1. #include <stdio.h>
2. #define SQUARE(x) x * x     
3. //这也是个替换,用后面的内容替换前面的宏
4. 
5. int main()
6. {
7.  int a = 5;
8.  printf("%d\n", SQUARE(a + 1));//打印结果为11
9.  return 0;
10. }

解析:打印结果,我们会理所应当的以为是36,但是结果却是11,因为代入后是a + 1* a+1,所以结果是11,要是想让结果为36,正确的应该是#define SQUARE(x)  ((x) * (x))

注意:定义宏,一定要注意括号,记得带上(大部分情况是需要带的,否则会出现优先级问题,导致想要的结果和预期的不一样)

比如上述代码,我们想要的是36,但是结果却是11

每一个部分带上括号,整体也要带上括号

3.2.3 #define 替换规则

先替换,后计算

在程序中扩展 #define 定义符号和宏时,需要涉及几个步骤。

1. 在调用宏时,首先对参数进行检查,看看是否包含任何由 #define 定义的符号。如果是,它们首先被替换。

2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。

3. 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果是,就重复上述处理过程。

注意:

1. 宏参数和 #define 定义中可以出现其他 #define 定义的符号。但是对于宏,不能出现递归。

2. 当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不被搜索(意思就是printf("M = %d", 10)中的M就不会被替换)。

相关文章
|
5月前
|
存储 自然语言处理 程序员
程序环境和预处理(1)
程序环境和预处理(1)
48 0
|
5月前
|
编译器 Linux C语言
程序环境和预处理(2)
程序环境和预处理(2)
37 0
|
6月前
|
存储 编译器 程序员
程序环境和预处理
程序环境和预处理
|
6月前
|
存储 编译器 程序员
零基础也能学会的『程序环境和预处理』
零基础也能学会的『程序环境和预处理』
|
6月前
|
存储 自然语言处理 编译器
程序环境和预处理(详解)
程序环境和预处理(详解)
|
存储 编译器 程序员
【C】程序环境和预处理
在ANSI C的任何一种实现中,存在两个不同的环境。
|
存储 自然语言处理 程序员
【程序环境与预处理】(一)
【程序环境与预处理】(一)
85 0
|
6月前
【程序环境与预处理玩转指南】(中)
【程序环境与预处理玩转指南】
|
6月前
|
存储 编译器 程序员
【程序环境与预处理玩转指南】(上)
【程序环境与预处理玩转指南】
|
6月前
|
编译器 Linux C++
【程序环境与预处理玩转指南】(下)
【程序环境与预处理玩转指南】