IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言程序的编译(预处理)概述 —— 上 -> 正文阅读

[C++知识库]C语言程序的编译(预处理)概述 —— 上

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

在 ANSI C 的任何一种实现中,存在两个不同的环境。

  1. 翻译环境,在这个环境中源代码被转换成可执行的机器指令
  2. 执行环境,它用于实际执行代码

2. 编译 + 链接概述

在我们写好代码并且 Ctrl + F5 会弹出一个黑框框,在我们的项目目录下会生成 .exe 可执行文件,我们现在要了解的就是这个东西是怎么被“造”出来的。

?

?2.1 翻译环境

我们大体上可以知道是,可执行文件是由编译、链接这两个部分组合产生。用文字阐述可以说:源文件(可以是多个)经过编译器编译产生目标文件,这些目标文件被送进链接库进行链接,从而产生可执行程序。

?

?

我们需要注意一个点,编译器的编译过程不止编译这一个环节。?也就是说,编译本身也可以分为几个阶段。

?

因为 Visual Studio 这些编译器是一个集成开发环境,所以不方便展示各个部分的详细内容。但是在以后学习 Linux 的过程中,我们回过头来是一定可以观察的。这里我就用文字阐述。

  • 在预编译阶段,编译器会将头文件的内容、define 定义的文本替换都写入 .i 文件当中,并且删除注释。也就是说,预编译产生的 .i 文件里面写的 C 语言代码,是包括头文件的所有 C 语言代码。
  • 编译阶段,从这个阶段开始,就要逐渐让计算机硬件能够识别我们的代码。所以这个阶段就会把 C 语言代码翻译成汇编语言代码。比如说我们有 int max = 10; ,那么编译阶段就会把 int 看成类型生成对应的汇编指令,max 是变量名,生成对应的汇编语句, = 是赋值符号,同样也会生成汇编语句,等等……这些汇编代码存放在 .s 文件中。
  • 汇编,这里就是生成目标文件的步骤。这个阶段会对汇编代码进行更加细化的拆分,生成最原始的机器指令,即二进制指令。这些指令会被放在 .obj 文件当中(Linux 环境下为 .o 文件),在 Linux 环境下可观察这些二进制指令,但我们的大脑会认为这些是乱码。

2.2 运行环境

程序的执行过程:

  1. 程序必须载入内存中。在具有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排(比如单片机中的烧写代码就需要借助电脑的操作系统),也可能是通过可执行代码置入只读内存来完成。
  2. 程序载入内存后,执行便开始,此时需要找到 main 函数并调用它。
  3. 程序代码被执行后,这时程序将使用一个运行时堆栈(函数栈帧),存储函数的局部变量和返回地址。程序同时也可以使用静态存储,存储于静态内存中的变量在程序的整个执行过程一直保留(全局变量)。?
  4. 终止程序,可能是 main 函数正常结束,也可能是意外结束。

3. 预处理概述

3.1 预定义符号

我们可以了解一些预定义符号:

__FILE__ ? ? ?//进行编译的源文件
__LINE__ ? ? //文件当前的行号
__DATE__ ? ?//文件被编译的日期
__TIME__ ? ?//文件被编译的时间

?我们可以搭配文件操作使用他们。

#include <stdio.h>

int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//格式化输出
	fprintf(pf, "file:%s line:%d date:%s time:%s\n", __FILE__, __LINE__, __DATE__, __TIME__);
	fclose(pf);
	pf = NULL;

	return 0;
}

此时在项目目录下生成的 .txt 文件记录着我们想要的东西:

?

3.2 #define

3.2.1 #define 定义标识符

定义标识符非常简单,也就是常说的文本替换。

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

// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define PRINT printf("%s\n","#define 定义标识符")

#include <stdio.h>

int main()
{
	PRINT;
	return 0;
}

?需要注意的是,在我们定义标识符时,尽量不要在后面添加分号,因为在我们书写代码时会习惯自己加上分号,如果定义的标识符后有分号,可能会产生不易察觉的错误。

#define WHILE while(1);//这里添加了分号
#include <stdio.h>
#include <windows.h>
int main()
{
	WHILE  //这里相当于 while(1);  循环执行了空语句,与程序本意不一致
	{
		printf("hello world!\n");
		Sleep(1000);
	}
	return 0;
}

?当然我们也可以使用这个功能来实现一些令人难以捉摸的骚操作,假设有一个程序员每次使用 swich 语句内的 case ,后面都要写 break,他觉得很麻烦,那么他便写出了这么一段代码:

#define CASE break;case

#include <stdio.h>

int main()
{
	int n = 3;
	switch (n)
	{
	case 1:
		CASE 2 :
			CASE 3 :
			printf("hello world!\n");
	CASE 4 :
		break;
	}
	return 0;
}

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:19:47  更:2022-07-21 21:21:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 6:29:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码