https://www.zhihu.com/people/jiu_sheng
https://blog.csdn.net/qianshang52013/article/details/138140235?spm=1001.2014.3001.5501
Autosar 系列教程:小柴带你学 AutoSar 总目录
# 小柴带你学 AutoSar 系列一、基础知识篇(4)编译
编译真的很重要!了解一下机器是如何工作的吧。当然啦!通过学习这篇文章还可以学习制作库文件哦!隐藏你的源码,依然可以调用函数。这样代码发给别人就不怕源码暴漏哦!🍔
# GCC 编译
GCC 编译器是一个开源的编译器套件,用于编译多种编程语言,包括 C、C++、Objective-C、Fortran、Ada 等。它是一个功能强大且灵活的编译器,支持多种平台和架构。
# 1. 预处理阶段(Preprocessing)
在预处理阶段,GCC 将源代码中的预处理指令(以 #
开头的指令,如 #include
、 #define
)处理成纯 C 代码。预处理器会将头文件包含到源文件中,执行宏替换等操作。
生成的文件:通常,预处理后的文件会以 .i
扩展名保存,如 source.i
。
# 2. 编译阶段(Compilation)
编译阶段将预处理后的源文件翻译成汇编语言。GCC 使用的是内置的 C 编译器(称为 cc1
),它将 C 代码转换成目标机器的汇编代码。
生成的文件:通常,编译后的文件会以 .s
扩展名保存,如 source.s
。
# 3. 汇编阶段(Assembly)
在汇编阶段,汇编器将汇编代码转换成机器码指令,生成目标文件。这些目标文件包含了二进制代码和相关的元数据。
生成的文件:通常,汇编后的文件会以 .o
扩展名保存,如 source.o
。
# 4. 链接阶段(Linking)
链接阶段将所有的目标文件(包括程序的主文件和所有的库文件)链接在一起,生成最终的可执行文件。链接器(ld)负责解析符号引用、符号重定位等任务,将各个目标文件中的代码和数据段组合成一个可执行文件。
生成的文件:最终生成的可执行文件通常没有扩展名,如 a.out
。
# 示例
假设有一个 C 源文件 hello.c
:
#include <stdio.h> | |
int main() { | |
printf("Hello, world!\n"); | |
return 0; | |
} |
通过 GCC 编译该文件时,可以执行以下命令:
gcc -o hello hello.c |
这个命令会依次执行预处理、编译、汇编和链接四个阶段,并生成最终的可执行文件 hello
。
hello.i
:预处理后的文件。hello.s
:编译后的汇编文件。hello.o
:汇编后的目标文件。hello
:最终的可执行文件。
这就是 GCC 编译器的工作过程以及每个阶段生成的文件。
# GHS 编译
RH850 是一种用于嵌入式系统的微控制器(MCU),通常用 Green Hills Software(GHS)编译工具进行编译和链接。GHS 工具链包含编译器、汇编器、链接器等,支持各种目标平台和架构。编译过程包括使用链接脚本,这是生成嵌入式系统二进制文件的关键步骤。下面是一个典型的 RH850 使用 GHS 编译器的编译过程,详细介绍了每个阶段及其生成的文件,特别是链接器脚本的作用。
# 1. 源代码准备
假设有一个简单的 C 源文件 main.c
:
#include <stdio.h> | |
void main(void) { | |
printf("Hello, RH850!\n"); | |
} |
# 2. 预处理阶段
预处理器将处理所有的预处理指令,例如 #include
和 #define
,生成一个预处理后的文件。
命令:
ccrh850 -E main.c -o main.i |
生成的文件:
main.i
:预处理后的文件。
# 3. 编译阶段
编译器将预处理后的 C 代码转换为汇编代码。
命令:
ccrh850 -S main.i -o main.s |
生成的文件:
main.s
:汇编代码文件。
# 4. 汇编阶段
汇编器将汇编代码转换为机器代码,生成目标文件。
命令:
asrh850 main.s -o main.o |
生成的文件:
main.o
:目标文件(对象文件)。
# 5. 链接阶段
链接器将多个目标文件和库文件链接在一起,生成可执行文件。链接器脚本在这个阶段非常重要,用于指定代码和数据段在内存中的布局。
链接器脚本(linker script)
一个典型的链接器脚本可能如下所示( linker.ld
):
SECTIONS | |
{ | |
.text : { | |
*(.text) | |
} | |
.data : { | |
*(.data) | |
} | |
.bss : { | |
*(.bss) | |
} | |
} |
这个脚本指定了 .text
段(代码段)、 .data
段(数据段)和 .bss
段(未初始化数据段)的布局。
命令:
librh850 main.o -T linker.ld -o main.elf |
生成的文件:
main.elf
:最终的可执行文件(ELF 格式)。
# 总结
预处理:
ccrh850 -E main.c -o main.i
- 生成预处理后的文件
main.i
。
- 生成预处理后的文件
编译:
ccrh850 -S main.i -o main.s
- 生成汇编代码文件
main.s
。
- 生成汇编代码文件
汇编:
asrh850 main.s -o main.o
- 生成目标文件
main.o
。
- 生成目标文件
链接:
librh850 main.o -T linker.ld -o main.elf
- 生成最终的可执行文件
main.elf
,使用链接器脚本linker.ld
指定内存布局。
- 生成最终的可执行文件
每个阶段都生成特定的中间文件和最终的可执行文件,链接器脚本在链接阶段至关重要,它定义了程序的内存布局,使得生成的二进制文件能够正确地运行在目标硬件上。
# 静态库.lib
通过静态库,开发者可以隐藏实现细节,仅暴露接口(头文件)。这增加了代码的安全性,并使得接口的更改不会影响到使用该库的代码,只要接口保持不变。
真的太好玩啦!
# 1. 制作静态库
VS 创建一个新项目
打印一些信息,并在头文件声明这个函数
- 生成 lib
看到他啦
# 2. 使用静态库
# 1. 创建或打开你的 C 项目
首先,确保你已经在 Visual Studio 中创建了一个 C 项目或打开了现有的 C 项目。
# 2. 将静态库文件添加到项目
假设你的静态库文件是 StaticLib1.lib
。
- 将
StaticLib1.lib
文件拷贝到你的项目目录中,通常放在一个特定的文件夹中,比如libs
文件夹。我懒哈哈!随便放啦
# 3. 添加库文件路径和库文件名到项目设置
- 打开项目属性:
- 在解决方案资源管理器中,右键点击你的项目名称,选择 “属性”(
Properties
)。
- 在解决方案资源管理器中,右键点击你的项目名称,选择 “属性”(
- 配置包含目录:
- 在项目属性窗口中,导航到
Configuration Properties -> C/C++ -> General
。 - 在
Additional Include Directories
字段中,添加你的头文件目录的路径。如果你的头文件在include
文件夹中,输入相对路径include
或者绝对路径。
- 在项目属性窗口中,导航到
- 配置库目录:
- 在项目属性窗口中,导航到
Configuration Properties -> Linker -> General
。 - 在
Additional Library Directories
字段中,添加你的静态库目录的路径。如果你的库文件在libs
文件夹中,输入相对路径libs
或者绝对路径。
- 在项目属性窗口中,导航到
- 添加库文件:
- 在项目属性窗口中,导航到
Configuration Properties -> Linker -> Input
。 - 在
Additional Dependencies
字段中,添加你的库文件名称mylibrary.lib
。如果有多个库文件,用分号分隔。
- 在项目属性窗口中,导航到
最后就可以调用静态库里的函数啦!同时又看不到函数的具体实现。是不是贼有意思呢?