当前位置: 首页 > news >正文

5 C 程序全流程解析:编写、预处理、编译、汇编、链接、运行与 GCC 指令详解

C 程序运行机制流程概述

        通过以上步骤,我们可以将一个 C 语言源代码文件逐步转换为一个可执行的二进制程序。这一过程涉及多个关键工具和步骤,每一步都承担着特定的任务,发挥着独特的作用。深入理解这些步骤,不仅有助于我们更好地掌握 C 语言编程的精髓,还能让我们对软件开发的基本原理有更透彻的认识。

        接下来,我将对这些步骤进行逐一细致的讲解,帮助大家更好地理解和应用。

1.1 编写代码

        首先,我们需要使用 C 语言编写源程序代码,并将其保存到磁盘文件中。

        源代码文件通常以 “.c” 作为扩展名。例如,我们创建了一个名为 main.c 的文件,内容如下:

#include <stdio.h>

int main()
{
    printf("Hello World");
    return 0;
}

1.2 预处理

        预处理是编译过程的第一步,由预处理器对源代码文件进行处理

        预处理的主要任务:

  • 去除多余的空格和注释,使代码更简洁
  • 处理预处理指令(如 #include、#define 等)。例如,#include <stdio.h> 会将标准输入输出库的头文件内容插入到代码中。
  • 生成一个经过处理的源代码文件,通常以 .i 作为扩展名。例如,main.c 经过预处理后会生成 main.i 文件。

        预处理确保了代码的一致性和可移植性,为后续编译做好准备。

1.3 编译

        编译是将预处理后的源代码(.i 文件)转换为汇编代码的过程。汇编代码是一种低级语言,与机器码非常接近。

        编译过程:

  • 编译器读取预处理后的文件(如 main.i),并将其翻译为汇编代码
  • 生成的汇编代码文件通常以 .s.asm 作为扩展名。例如,main.i 经过编译后会生成 main.s 文件。

        编译是将高级语言(如 C 语言)转换为低级语言的关键步骤,使代码能够在特定的硬件平台上运行。

1.4 汇编

        汇编是将汇编代码转换为机器码的过程机器码是计算机可以直接执行的二进制指令

        汇编过程:

  • 汇编器读取汇编代码文件(如 main.s),并将其翻译为机器码
  • 生成的目标文件是二进制文件,通常以 .o(在 Unix-like 系统上)或 .obj(在 Windows 上)作为扩展名。例如,main.s 经过汇编后会生成 main.o 或 main.obj 文件。

        汇编过程将汇编语言转换为机器语言,使代码能够在计算机上直接执行。

1.5 链接

        链接是将多个目标文件(如 main.o)与库文件(如 C 标准库)合并,生成最终的可执行文件的过程

        链接过程:

  • 链接器读取目标文件和库文件,解析它们之间的依赖关系
  • 库文件由系统提供,包含标准函数和数据结构(如 <stdio.h> 中的 printf 函数)。
  • 生成的最终可执行文件在 Windows 上通常以 .exe 作为扩展名,在 Unix-like 系统上则没有扩展名(如 a.out 自定义名称

        链接过程确保了程序能够正确调用库函数,并生成一个完整的可执行文件。

1.6 运行

        运行是执行生成的可执行文件,验证程序的正确性和功能性的过程。

        运行过程:

  • 在命令行或终端中运行可执行文件(如 ./a.outmain.exe .\main.exe)。
  • 程序输出结果(如 Hello World),验证其是否按预期工作。

        运行过程是开发周期的最后一步,确保程序能够正确完成其功能。

1.7 流程总结

步骤文件名描述
1. 编写代码main.c使用 C 语言编写源代码,并保存为 .c 文件。
2. 预处理main.i预处理器处理源代码,去除注释和空格,处理预处理指令,生成 .i 文件
3. 编译main.s / main.asm编译器将预处理后的代码翻译成汇编代码,生成 .s 或 .asm 文件
4. 汇编main.o / main.obj汇编器将汇编代码翻译成机器码,生成目标文件 .o(Unix-like)或 .obj(Windows)
5. 链接main.exe / 可执行文件链接器将目标文件和库文件合并,生成最终的可执行文件(如 .exe 或无扩展名的可执行文件)
6. 运行无特定文件名执行生成的可执行文件,验证程序功能。

2 C 程序运行机制流程演示

        在通常的开发环境中,运行 C 源文件时,编译过程中产生的中间文件通常不会被保存,只会生成最终的可执行二进制程序(.exe 文件)。

        然而,为了更深入地了解编译过程并保留中间文件,我们可以使用命令行工具(如终端)来手动执行预处理、编译、汇编和链接等步骤。

        以下是具体步骤:

2.1 打开命令行终端

        我们将使用 VS Code 的集成终端作为命令行工具。如下图所示,打开 VS Code 的终端工具,以便我们可以输入和执行各种命令。

2.2 预处理指令示例

        在终端中输入以下命令并按回车键运行:

gcc -E main.c -o main.i
  • gcc:调用 GCC 编译器。
  • -E指示编译器只执行预处理步骤,而不进行编译、汇编和链接。预处理步骤包括去除注释、处理预处理指令(如 #include、#define 等)。
  • main.c:指定要处理的源代码文件。
  • -o main.i:指定预处理后输出文件的名称为 main.i。

        运行完成后,会生成 main.i 文件,这是经过预处理的源文件,如下图所示:

        如果省略了 -o main.i,GCC 会将预处理后的输出默认打印到标准输出(通常是终端),而不是保存到文件中。具体来说:

  • 默认行为:预处理后的代码会被输出到终端窗口,而不是保存为文件。
  • 不便之处
    • 你无法直接保存预处理后的代码供后续步骤(如编译、汇编)使用。
    • 如果预处理后的代码很长,终端窗口可能无法完整显示。
    • 不便于自动化处理,因为输出没有保存到文件中。

        通过查看 main.i 文件,可以发现其中包含了大量标准库头文件的内容,例如 stdio.h。在原始的 .c 源文件中,这些内容仅通过 #include 指令进行引用。经过预处理后,这些头文件的内容被直接插入到 .i 文件中,因此导致文件大小显著增加

 2.3 编译指令示例

        在终端中输入以下命令并按回车键运行:

gcc -S main.i -o main.s
  • gcc:调用 GCC 编译器。
  • -S指示编译器只执行到汇编步骤,而不进行后续的汇编和链接。这意味着编译器会将预处理后的代码(.i 文件)翻译成汇编代码。
  • main.i:指定要处理的输入文件,即预处理后的文件。
  • -o main.s:指定输出文件的名称为 main.s,即汇编代码文件。

        运行完成后,会生成 main.s 文件,这是汇编文件,如下图所示:

        如果省略了 -o main.s,GCC 会将生成的汇编代码默认输出到一个名为 main.s 的文件中,但这个默认行为取决于输入文件的名称。具体来说:

  • 默认行为:如果输入文件名是 main.i,那么默认情况下,生成的汇编代码会被保存到 main.s 文件中。
  • 灵活性:省略 -o 选项时,输出文件名是基于输入文件名自动生成的,这可能会在某些情况下导致文件名冲突或不符合预期。
  • 明确性:使用 -o 选项可以明确指定输出文件名,避免任何潜在的混淆或错误。

2.4 汇编指令示例

        在终端中输入以下命令并按回车键运行:

gcc -c main.s -o main.o
  • gcc:调用 GCC 编译器。
  • -c指示编译器只执行到汇编后的目标文件生成步骤,而不进行后续的链接。这意味着编译器会将汇编代码(.s 文件)翻译成目标文件(.o 文件)。
  • main.s:指定要处理的输入文件,即汇编代码文件。
  • -o main.o:指定输出文件的名称为 main.o,即目标文件。 

        运行完成后,会生成 main.o 文件,这是目标文件,如下图所示:

        如果省略了 -o main.o,GCC 会将生成的目标文件默认输出到一个基于输入文件名自动生成的文件中。具体来说:

  • 默认行为:如果输入文件名是 main.s,那么默认情况下,生成的目标文件会被保存到 main.o 文件中。
  • 灵活性:省略 -o 选项时,输出文件名是基于输入文件名自动生成的,这可能会在某些情况下导致文件名冲突或不符合预期。
  • 明确性:使用 -o 选项可以明确指定输出文件名,避免任何潜在的混淆或错误。

2.5 链接指令示例

        在终端中输入以下命令并按回车键运行:

gcc main.o -o main.exe
  • gcc:调用 GCC 编译器。
  • main.o指定要处理的目标文件,即经过汇编步骤生成的中间文件。
  • -o main.exe:指定输出文件的名称为 main.exe,即可执行文件。 

        运行完成后,会生成 main.exe 文件,这是最终的可执行文件,如下图所示:

        如果省略了 -o main.exe,GCC 会将生成的可执行文件默认输出到一个名为 a.out 的文件中(在 Unix-like 系统上,如 Linux 和 macOS)。在 Windows 系统上,默认的可执行文件名可能是 a.exe,但这取决于具体的 GCC 配置

  • 默认行为
    • 在 Unix-like 系统上,省略 -o 选项时,生成的可执行文件会被保存到 a.out 文件中。
    • 在 Windows 系统上,生成的可执行文件可能会被保存到 a.exe 文件中。
  • 灵活性:省略 -o 选项时,输出文件名是固定的,这可能会在某些情况下导致文件名冲突或不符合预期。
  • 明确性:使用 -o 选项可以明确指定输出文件名,避免任何潜在的混淆或错误。

        如果在使用 gcc main.o -o main 命令时省略了输出文件的后缀名,GCC 会根据系统的默认行为来决定生成的可执行文件的名称和类型

  1. Unix-like 系统(如 Linux 和 macOS)
    • 省略后缀名时,GCC 会生成一个名为 main 的可执行文件。在 Unix-like 系统上,可执行文件不需要特定的后缀名,系统通过文件的权限和属性来识别可执行文件。
    • 你可以通过 ./main 来运行生成的可执行文件。
  2. Windows 系统
    • 在 Windows 系统上,省略后缀名可能会导致混淆,因为 Windows 通常通过文件后缀名来识别文件类型。
    • 在 Windows 上,如果是在类似 Unix 的环境下(如 Cygwin 或 MinGW),它也可能没有扩展名,但如果直接从命令提示符 cmd 或 PowerShell 运行,可能需要指定 .exe 扩展名或使用关联程序来运行它)。
    • 请注意,在 Windows 的某些配置中(特别是当 GCC 被配置为生成 Windows 可执行文件时),即使没有指定 .exe 扩展名,GCC 也可能会自动添加它,但这不是跨平台的做法
    • 为了避免混淆和确保兼容性,建议在 Windows 上也使用 .exe 后缀名。

2.6 运行指令示例

        在终端中输入以下命令并按回车键运行:

./main.exe  或 .\main.exe 

        在 Windows 的 PowerShell 中,./main.exe 和 .\main.exe 实际上是等效的,都可以用来执行当前目录下的可执行文件。这种差异主要源于不同操作系统和 shell 的惯例,但在 PowerShell 中,它们被同样地处理。 

  1. ./main.exe
    • 这种形式更常见于类 Unix 系统(如 Linux 和 macOS)的 shell(如 bash、zsh 等)。在这些系统中,. 表示当前目录,./ 则明确指定了当前目录下的文件
    • 在 PowerShell 中,虽然 ./ 不是传统上用于指定当前目录的前缀,但 PowerShell 解释器足够智能,能够识别并处理这种形式,将其视为与 .\ 相同。
  2. .\main.exe
    • 这是 PowerShell 中更常见的形式,用于指定当前目录下的可执行文件。
    • .\ 明确指示 PowerShell 在当前目录中查找并执行名为 main.exe 的可执行文件
  3. 建议
    • 在 PowerShell 中,建议使用 .\ 来指定当前目录下的可执行文件,因为这是 PowerShell 的原生和首选形式。
    • 虽然 ./ 在 PowerShell 中也能工作,但为了避免混淆和潜在的兼容性问题(特别是在跨平台脚本中),最好坚持使用 PowerShell 的惯例。

        输入执行命令后,这会执行 main.exe 文件,终端中会显示 “Hello World”。

        在使用 VS Code 开发 C/C++ 程序时,编译生成的可执行文件(如 main.exe)可能无法直接在终端中运行,出现类似以下的错误信息: 

错误原因

  1. 路径问题
    • 当你输入 main.exe 时,PowerShell 会在系统的环境变量 PATH 中指定的目录中查找这个可执行文件,而不是在当前工作目录中查找
    • 如果 main.exe 不在 PATH 中的任何目录中,PowerShell 就会抛出 CommandNotFoundException,提示找不到命令。
  2. 当前目录的特殊性
    • 在 PowerShell 中,当前目录(.)默认不在 PATH 中,这是出于安全考虑,防止执行当前目录下的恶意脚本或可执行文件。

解决方案

  1. 使用相对路径

    • 在当前目录下运行可执行文件时,使用 .\main.exe,其中 .\ 表示当前目录
    • 这样做可以明确告诉 PowerShell 在当前目录中查找 main.exe。
  2. 修改 PowerShell 配置(不推荐)

    • 理论上,你可以通过修改 PowerShell 的配置来将当前目录添加到 PATH 中,但这通常不推荐,因为它会降低系统的安全性。

3 GCC 常用指令

        GCC(GNU Compiler Collection)是 GNU 项目开发的编程语言编译器集合,广泛用于 C、C++ 等多种编程语言的编译。以下是 GCC 在 C 语言编译过程中常用的指令,以及一些其他实用的指令。

3.1 各阶段单独使用的指令

预处理阶段:gcc -E main.c -o main.i
    -E:指示 GCC 只执行预处理步骤
        生成预处理后的源代码文件(通常以 .i 为扩展名)

编译阶段:gcc -S main.i -o main.s
    -S:指示 GCC 将预处理后的源代码编译成汇编代码
        生成汇编代码文件(通常以 .s 或 .asm 为扩展名)

汇编阶段:gcc -c main.s -o main.o
    -c:指示 GCC 将汇编代码汇编成目标文件
        在 Unix-like 系统上通常为 .o 文件
        在 Windows 上通常为 .obj 文件

链接阶段:gcc main.o -o executable
    无特定选项,GCC 默认执行链接步骤,将目标文件与必要的库文件合并,生成最终的可执行文件
        在 Windows 上通常为 .exe 文件
        在 Unix-like 系统上可能没有扩展名

3.2 一次到位的编译指令 

        为了方便起见,GCC 也支持一次到位的编译指令,即从源代码直接生成可执行文件,无需手动执行每个阶段的指令。

gcc source.c -o executable
  • source.c指定要编译的源代码文件
  • -o executable指定输出可执行文件的名称。如果省略,GCC 将生成一个默认名称的可执行文件(如 a.out 或 a.exe)。

3.3 其他常用指令

查看 GCC 版本:

gcc --version

优化编译:

gcc -O2 source.c -o executable
  • -O2:启用第二级优化,可以生成更快、更小的可执行文件。GCC 还支持 -O0(无优化,默认)、-O1(第一级优化)、-O3(第三级优化,通常比 -O2 更激进)等选项。

生成调试信息:

gcc -g source.c -o executable
  • -g:生成调试信息,使得生成的可执行文件可以使用调试器(如 GDB)进行调试。

指定包含目录:

gcc -I/path/to/include source.c -o executable
  • -I:指定额外的包含目录,GCC 将在这些目录中查找头文件。

指定库文件目录:

gcc -L/path/to/lib source.c -o executable -lmylib
  • -L:指定额外的库文件目录,GCC 将在这些目录中查找库文件。
  • -lmylib:链接名为 libmylib.a 或 libmylib.so 的库文件。

相关文章:

  • 【C++ Qt】认识Qt、Qt 项目搭建流程(图文并茂、通俗易懂)
  • 【Spring】IoC详解:方法Bean的存储、Bean重命名、扫描路径@Component(下)
  • 计算机操作系统——存储器管理
  • Android 日志输出模块
  • 现在AI大模型能帮做数据分析吗?
  • ScholarCopilot:“学术副驾驶“
  • 【数据结构】励志大厂版·初阶(复习+刷题):复杂度
  • SpringBoot条件注解全解析:核心作用与使用场景详解
  • STM32 HAL库 ADC+TIM+DMA 3路 1S采样一次电压
  • C++对象池设计:从高频`new/delete`到性能飞跃的工业级解决方案
  • pycharm中调试功能讲解
  • PGAdmin下载、安装、基础使用
  • Oracle OCP知识点详解3:使用 vim 编辑文件
  • Web 前端技术解析:构建高效、动态的用户体验
  • qt中,父类中有Q_OBJECT,子类中还需要加Q_OBJECT吗
  • Shell的运行原理以及Linux当中权限问题
  • Flutter 文本组件深度剖析:从基础到高级应用
  • TGCTF web
  • SQL刷题日志(day1)
  • 狂神SQL学习笔记二:安装MySQL
  • 宁夏民政厅原厅长欧阳艳已任自治区政府副秘书长、办公厅主任
  • 王星昊再胜连笑,夺得中国围棋天元赛冠军
  • A股三大股指收跌:地产股领跌,银行股再度走强
  • 为何未来的福利国家必须绿色且公平
  • 在县中,我看到“走出去”的渴望与“留下来”的惯性
  • 韩国检方重启调查金建希操纵股价案