GCC编译(1)入门概述

GCC编译(1)入门概述

Author: Once Day Date: 2026年2月13日

一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦...

漫漫长路,有人对你微笑过嘛...

全系列文章可参考专栏: 编译构建工具链_Once-Day的博客-CSDN博客

参考文章:

文章目录

  • GCC编译(1)入门概述
        • [1. GCC概述](#1. GCC概述)
        • [2. GCC编译流程](#2. GCC编译流程)
        • [3. GCC 编译组件](#3. GCC 编译组件)
        • [4. GCC工具组件](#4. GCC工具组件)
        • [5. GCC和G++的区别](#5. GCC和G++的区别)
        • [6. 指定C/C++编译版本](#6. 指定C/C++编译版本)
        • [7. GCC一步编译和分布编译](#7. GCC一步编译和分布编译)
          • [5.1 预处理选项(gcc -E)](#5.1 预处理选项(gcc -E))
          • [5.2 编译成汇编代码文件(gcc -S)](#5.2 编译成汇编代码文件(gcc -S))
          • [5.3 编译成目标文件(gcc -c)](#5.3 编译成目标文件(gcc -c))
          • [5.4 链接指定文件](#5.4 链接指定文件)
          • [5.5 一些常用的其他gcc命令](#5.5 一些常用的其他gcc命令)
        • [8. GCC编译静态库和动态库](#8. GCC编译静态库和动态库)
1. GCC概述

GCC 最初的全称为 GNU C Compiler,诞生于 GNU 计划早期阶段,目标是为自由软件生态提供一款高质量、可移植的 C 语言编译器。在 20 世纪 80 年代末,商业编译器占据主导地位,GCC 的出现不仅填补了自由编译工具的空白,也为后续类 Unix 系统的软件构建奠定了技术基础。其早期设计强调标准兼容性与跨平台能力,这一理念至今仍贯穿整个体系结构。

随着软件工程规模的扩大,单一语言工具链已难以满足需求。GCC 逐步扩展前端架构,支持 C++、Objective-C、Fortran、Go 等多种语言,名称也演进为 GNU Compiler Collection。其内部通过统一的中间表示 GIMPLE 和后端优化框架实现语言解耦,使不同语言能够共享优化与代码生成模块。这种"多前端、共享后端"的架构设计,是 GCC 能长期演进的重要原因。

在主流 Linux 发行版中,GCC 通常作为基础开发工具预装,配合 glibcbinutils 构成完整构建环境。可以通过如下方式查看版本信息:

bash 复制代码
gcc --version

若系统默认版本偏低,开发者可手动编译安装新版本 GCC。该过程通常需要现有 GCC 参与构建,并依赖 gmpmpfrmpc 等数学库。典型流程如下:

bash 复制代码
./configure --prefix=/usr/local/gcc-13
make -j$(nproc)
make install

由于编译阶段包含多语言前端与优化器构建,整体耗时较长,对硬件资源要求较高。在工程实践中,往往通过软件仓库升级、容器环境或预编译工具链来替代源码编译,以提高效率并降低维护成本。

2. GCC编译流程

C语言编译流程如下所示:

gcc 在编译 C 语言程序时,本质上执行的是一个分阶段的转换流程,将源代码逐步转化为可执行的机器指令。整个过程可以划分为预处理、编译、汇编和链接四个主要阶段,每个阶段都有明确的输入与输出形式,并由不同的工具协同完成。

第一阶段是预处理,由 cpp 完成。它负责展开宏定义、处理 #include 头文件、条件编译指令以及删除注释等操作。该阶段并不生成机器代码,而是输出一个纯净的中间文本文件,通常以 .i 结尾。可以使用如下命令单独查看预处理结果:

bash 复制代码
gcc -E main.c -o main.i

第二阶段是编译阶段,gcc 会将预处理后的 C 代码转换为汇编代码。在此过程中,语法分析、语义分析以及中间表示优化都会完成。优化级别(如 -O2)也主要在这一阶段发挥作用。生成的汇编文件通常以 .s 结尾:

bash 复制代码
gcc -S main.i -o main.s

第三阶段是汇编阶段,由 as 将汇编代码翻译为目标文件,即二进制形式的 .o 文件。此时生成的目标文件已包含机器指令,但尚未解决符号引用问题:

bash 复制代码
gcc -c main.s -o main.o

最后是链接阶段,由 ld 将多个目标文件与标准库(如 libc)进行符号解析与地址重定位,生成最终可执行文件。默认情况下,gcc 会自动链接标准 C 运行时库:

bash 复制代码
gcc main.o -o main

整个流程可以抽象为:
main.c
Preprocess
Compile
Assemble
Link
Executable

这种分阶段设计不仅便于调试与问题定位,也使开发者可以通过不同选项精细控制构建行为。

3. GCC 编译组件
组件部分 描述
c++ gcc 的一个版木,默认语言设置为 C++,而且在连接的时候自动包含标准 C++ 库。这和 g++ 一样
ccl 实际的C编译程序
cclplus 实际的 C++ 编泽程序
collect2 在不使用 GNU 连接程序的系统上,有必要运行 collect2 来产生特定的全局初始化代码(例如 C++ 的构造函数和析构函数)
configure GCC 源代码树根目录中的一个脚木。用于设置配置值和创建GCC 编译程序必需的 make 程序的描述文件
crt0.o GCC 源代码树根目录中的一个脚木。用于设置配置值和创建GCC 编译程序必需的 make 程序的描述文件
cygwin1.dll Windows 的共享库提供的 API,模拟 UNIX 系统调用
f77 该驱动程序可用于编译 Fortran
f771 实际的 Fortran 编译程序
g++ gcc 的一个版木,默认语言设置为 C++,而且在连接的时候自动包含标准 C++ 库。这和 c++ 一样
gcc 该驱动程序等同于执行编译程序和连接程序以产生需要的输出
gcj 该驱动程序用于编译 Java
gnat1 实际的 Ada 编译程序
gnatbind 一种工具,用于执行 Ada 语言绑定
gnatlink 一种工具,用于执行 Ada 语言连接
jc1 实际的 Java 编译程序
libgcc 该库包含的例程被作为编泽程序的一部分,是因为它们可被连接到实际的可执行程序中。 它们是特殊的例程,连接到可执行程序,来执行基木的任务,例如浮点运算。这些库中的例程通常都是平台相关的
libgcj 运行时库包含所有的核心 Java 类
libobjc 对所有 Objective-C 程序都必须的运行时库
libstdc++ 运行时库,包括定义为标准语言一部分的所有的 C++ 类和函数
4. GCC工具组件
工具软件 描述
addr2line 给出一个可执行文件的内部地址,addr2line 使用文件中的调试信息将地址翻泽成源代码文件名和行号。该程序是 binutils 包的一部分
ar -11
as GNU 汇编器。实际上它是一族汇编器,因为它可以被编泽或能够在各种不同平台上工作。 该程序是 binutils 包的一部分
autoconf 产生的 shell 脚木自动配置源代码包去编泽某个特定版木的 UNIX
c++filt 程序接受被 C++ 编泽程序转换过的名字(不是被重载的),而且将该名字翻泽成初始形式。 该程序是 binutils 包的一部分
f2c 是 Fortran 到C的翻译程序。不是 GCC 的一部分
gcov gprof 使用的配置工具,用来确定程序运行的时候哪一部分耗时最大
gdb GNU 调试器,可用于检查程序运行时的值和行为
GNATS GNU 的调试跟踪系统(GNU Bug Tracking System)。一个跟踪 GCC 和其他 GNU 软件问题的在线系统
gprof 该程序会监督编泽程序的执行过程,并报告程序中各个函数的运行时间,可以根据所提供 的配置文件来优化程序。该程序是 binutils 包的一部分
ld GNU 连接程序。该程序将目标文件的集合组合成可执行程序。该程序是 binutils 包的一部
libtool 一个基本库,支持 make 程序的描述文件使用的简化共享库用法的脚木
make 一个工具程序,它会读 makefile 脚木来确定程序中的哪个部分需要编泽和连接,然后发布必要的命令。它读出的脚木(叫做 makefile 或 Makefile)定义了文件关系和依赖关系。
nlmconv 将可重定位的目标文件转换成 NetWare 可加载模块(NetWare Loadable Module, NLM)。该 程序是 binutils 的一部分
nm 列出目标文件中定义的符号。该程序是 binutils 包的一部分
objcopy 将目标文件从一种二进制格式复制和翻译到另外一种。该程序是 binutils 包的一部分
objdump 显示一个或多个目标文件中保存的多种不同信息。该程序是 binutils 包的一部分
ranlib 创建和添加到 ar 文档的索引。该索引被 Id 使用来定位库中的模块。该程序是 binutils 包的一部分
ratfor Ratfor 预处理程序可由 GCC 激活,但不是标准 GCC 发布版的一部分
readelf 从 ELF 格式的目标文件显示信息。该程序是 binutils 包的一部分
size 列出目标文件中每个部分的名字和尺寸。该程序是 binutils 包的一部分
strings 浏览所有类型的文件,析取出用于显示的字符串。该程序是 binutils 包的一部分
strip 从目标文件或文档库中去掉符号表,以及其他调试所需的信息。该程序是 binutils 包的一部分
vcg Ratfor 浏览器从文木文件中读取信息,并以图表形式显示它们。而 vcg 工具并不是 GCC 发布中的一部分,但 -dv 选项可被用来产生 vcg 可以理解的优化数据的格式
windres Window 资源文件编泽程序。该程序是 binutils 包的一部分
5. GCC和G++的区别

在 GNU 工具链中,gccg++ 本质上共用同一套编译框架与后端优化器,但它们的"默认行为"不同。两者都可以编译 C++ 代码,但 g++ 是为 C++ 语言场景定制的驱动程序,在编译阶段选择 C++ 前端,在链接阶段自动引入 C++ 运行时库。这种差异主要体现在默认参数与链接策略上,而非底层实现能力的不同。

从语言识别角度看,gcc 根据文件扩展名判断使用的前端,例如 .c 使用 C 前端,.cpp 使用 C++ 前端。但如果强制指定参数,二者可以互相替代。例如:

bash 复制代码
gcc -xc++ demo.cpp

上述命令等价于使用 C++ 前端进行编译。也就是说,g++ 在语法分析与代码生成层面并不具备"额外能力",其优势在于简化使用流程。

真正关键的差异发生在链接阶段。C++ 程序通常依赖 libstdc++ 以及 C++ 运行时支持库,而 g++ 会自动添加 -lstdc++ 和相关启动文件;gcc 在链接时则默认按 C 程序处理,不会自动链接 C++ 标准库。这也是为何直接使用 gcc demo.cpp 可能出现未定义引用错误。等价关系可以近似表示为:

bash 复制代码
g++ demo.cpp
≈ gcc -xc++ demo.cpp -lstdc++ -shared-libgcc

在实际工程中,若项目包含 C++ 代码,通常统一使用 g++ 作为链接驱动,以避免遗漏标准库依赖;而在混合 C/C++ 项目中,也可以显式控制编译与链接阶段,例如分别使用 gcc -c 编译 C 文件,再用 g++ 完成最终链接。这种策略能够确保符号解析与运行时环境匹配。

6. 指定C/C++编译版本

在实际开发中,GCC 允许通过标准选项显式指定 C 或 C++ 语言标准版本,从而控制语法特性与编译行为。这种机制对于保证跨平台一致性与老项目兼容性尤为重要。不同标准版本在语义规则、关键字支持以及库特性上存在明显差异,因此在构建系统中显式声明标准版本已成为工程实践中的基本要求。

对于 C 语言,可通过 -std= 选项指定标准版本,例如 c89c99c11c17 以及较新的 c23。示例如下:

bash 复制代码
gcc -std=c11 main.c -o main

若希望启用 GNU 扩展(如语句表达式、内联汇编扩展等),可以使用 gnu11gnu17 等模式。gnuXX 在对应 ISO 标准基础上增加 GNU 特性,而 cXX 则严格遵循标准。

对于 C++,同样使用 -std= 参数控制版本,例如 c++98c++11c++14c++17c++20c++23。示例:

bash 复制代码
g++ -std=c++20 demo.cpp -o demo

若项目依赖 GNU 扩展(如 typeof 或部分实验性特性),可以选择 gnu++20 等模式。需要注意,标准版本的选择不仅影响语法,还会影响标准库接口可见性,例如 <filesystem> 需要 c++17 及以上。

在大型项目中,通常在构建系统(如 MakefileCMake)中统一指定标准版本,以避免不同模块编译选项不一致导致 ABI 或行为差异。例如:

makefile 复制代码
CXXFLAGS = -std=c++17 -O2 -Wall

常见版本对C语言编译标准的支持:

GCC版本 c89/c90 c99 c11 c17 GNU90 GNU99 GNU11 GNU17
8.4以上 c89/c90 c99 c11 c17/c18 gnu90/gnu89 gnu99 gnu11 gnu17/gnu18
7.5-5.5 c89/c90 c99 c11 gnu90/gnu89 gnu99 gnu11
4.9.4-4.8.5 c89/c90 c99 c11 gnu90/gnu99 gnu99 gnu11
4.7.4以下 c89/c90 c99(部分支持) c11(部分支持) gnu90/gnu89 gnu99(部分支持) gnu11(部分支持)

常见版本对C++语言标准的支持程度:

GCC版本 c++98/03 c++11 c++14 c++17 GNU++98 GNU++11 GNU++14 GNU++17
8.4以上 c++98/c++03 c++11 c++14 c++17 gnu++98/gnu++03 gnu++11 gnu++14 gnu++17
7.5-5.5 c++98/c++03 c++11 c++14 c++1z(部分支持) gnu++98/gnu++03 gnu++11 gnu++14 gnu++1z(部分支持)
4.9.4-4.8.5 c++98/c++03 c++11 c++1y(部分支持) gnu++98/gnu++03 gnu++11 gnu++1y(部分支持)
4.7.4 c++98 c++11(部分支持) gnu++98 gnu++11(部分支持)
7. GCC一步编译和分布编译

直接使用 gcc demo.c 时,编译器会自动完成预处理、编译、汇编与链接全过程,并在当前目录生成默认可执行文件 a.out。这种"一步到位"的方式适合小型示例或临时测试。通过 -o 选项可以指定输出文件名,使构建结果更具可读性与可管理性:

bash 复制代码
gcc demo.c -o demo

上述命令本质上仍然包含完整链接过程,只是改变了最终产物名称。

在工程实践中,往往采用分步编译模式以提升构建效率与可维护性。首先生成头文件依赖关系,用于支持增量编译。当某个头文件变更时,仅重建相关目标文件,而无需全量重编译。常见做法如下:

bash 复制代码
gcc -MMD -MP -c demo.c -o demo.o

其中 -MMD 会生成 .d 依赖文件,记录头文件引用关系,便于 make 自动追踪依赖。

第二步生成目标文件(.o),此阶段仅进行编译与汇编,不做链接:

bash 复制代码
gcc -c demo.c -o demo.o

目标文件中包含机器指令与未解析符号信息,可与其他模块独立编译生成的 .o 文件组合使用。

最后由链接器统一处理多个目标文件与库文件,完成符号解析与重定位,生成可执行程序:

bash 复制代码
gcc demo.o util.o -o app

这种分步流程在多文件项目中尤为关键,可以显著减少重复编译时间,同时为后续引入静态库或动态库提供良好的结构基础。

5.1 预处理选项(gcc -E)

预处理过程会处理预处理命令,删除注释等。展开成完整的源文件。

通常可由以下几种形式:

shell 复制代码
gcc -E demo.c                #直接输出到终端上
gcc -E demo.c -o demo.i      #直接输出到指定文件中
gcc -E -C demo.c -o demo.i   #取消删除源文件中的注释代码

gcc -E支持的常用选项:

选项 功能
-D name[=definition] 在处理源文件之前,先定义宏 name。宏 name 必须是在源文件和头文件中都没有被定义过的。将该选项搭配源代码中的#ifdef name命令使用,可以实现条件式编译。如果没有指定一个替换的值(即省略 =definition),该宏被定义为值 1。
-U name 如果在命令行或 GCC 默认设置中定义过宏 name,则"取消"name 的定义。-D 和 -U 选项会依据在命令行中出现的先后顺序进行处理。
-include file 如同在源代码中添加 #include "file" 一样。
-l dir 同时适用于以引号 "" 和 <> 导入的头文件。当 GCC 在 -iquote 指令指定的目录下搜索头文件失败时,会再自动去 -I 指定的目录中查找。该选项在 GCC 10.1 版本中已被弃用,并建议用 -iquote 选项代替。
-isystem dir 指定搜索头文件的目录
-idirafter dir 指定搜索头文件的目录

其中,对于指定 #include 搜索路径的几个选项,作用的先后顺序如下:

  • 对于用 #include "" 引号形式引入的头文件,首先搜索当前程序文件所在的目录

  • 其次再前往 -iquote 选项指定的目录中查找;

  • 前往 -I 选项指定的目录中搜索;

  • 前往 -isystem 选项指定的目录中搜索;

  • 前往默认的系统路径下搜索;

  • 前往 -idirafter 选项指定的目录中搜索。

5.2 编译成汇编代码文件(gcc -S)

所谓编译,简单理解就是将预处理得到的程序代码,经过一系列的词法分析、语法分析、语义分析以及优化,加工为当前机器支持的汇编代码。

通常指令有以下几种形式:

shell 复制代码
gcc -S demo.c            #直接在当前目录下生成一个(同名.s)文件
gcc -S demo.c -o test.s  #指定输出文件的名字

该指令可以处理非预处理过的源代码或者经过预处理后的源代码

可通过以下选项为汇编代码添加必要的注释:

shell 复制代码
gcc -S demo.c -o test.s -fverbose-asm
5.3 编译成目标文件(gcc -c)

汇编其实就是将汇编代码转换成可以执行的机器指令。大部分汇编语句对应一条机器指令,有的汇编语句对应多条机器指令。相对于编译操作,汇编过程会简单很多,它并没有复杂的语法,也没有语义,也不需要做指令优化,只需要根据汇编语句和机器指令的对照表一一翻译即可。

编译级别及以上阶段的代码皆可以用作目标文件

shell 复制代码
gcc -c demo.c -o test.o  #输出到指定文件
gcc -c demo.s -o demo.o  #输出到指定文件
5.4 链接指定文件

对目标文件进行链接:

shell 复制代码
gcc demo.o -o demo.exe

除开libc库外,其他库都需要手动添加链接库。

静态链接都放在libc.a(achieve,获取),或共享的动态链接文件libc.so中(文件名后缀.so,代表share object,共享对象)。这些链接库一般位于/lib/或/usr/lib/,位于GCC默认的搜索的其他目录。

链接标准数学库文件:

shell 复制代码
gcc main.c -o main.out -lm

数学库的文件名是libm.a。前缀lib和后缀.a是标准的,m是基本名称,GCC 会在-l选项后紧跟着的基本名称的基础上自动添加这些前缀、后缀,本例中,基本名称为 m

链接其他目录中的库:

(1)把链接库作为一般的目标文件,为 GCC 指定该链接库的完整路径与文件名。

shell 复制代码
gcc demo.c -o demo.out /usr/lib/libm.a

(2)使用-L选项,为 GCC 增加另一个搜索链接库的目录:

shell 复制代码
gcc demo.c -o demo.out -L/usr/lib -lm  

可以使用多个-L选项,或者在一个-L选项内使用冒号分割的路径列表。

(3)把包括所需链接库的目录加到环境变量 LIBRARYPATH 中。

5.5 一些常用的其他gcc命令
命令选项 描述和解释
-ansi 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 例如 asm 或 typeof 关键词。
-DMACRO 以字符串"1"定义 MACRO 宏。
-DMACRO=DEFN 以字符串"DEFN"定义 MACRO 宏。
-IDIRECTORY 指定额外的头文件搜索路径DIRECTORY。
-c 指定额外的函数库搜索路径DIRECTORY。
-lLIBRARY 连接时搜索指定的函数库LIBRARY。
-m486 针对 486 进行代码优化。
-O0 不进行优化处理。
-O 或 -O1 优化生成代码。
-O2 进一步优化。
-O3 比 -O2 更进一步优化,包括 inline 函数。
-shared 生成共享目标文件。通常用在建立共享库时。
-static 禁止使用共享连接。
-UMACRO 取消对 MACRO 宏的定义。
-w 不生成任何警告信息。
-Wall 生成所有警告信息。
-x 指定输出文件的语言,可选包括c、c++、assembler、none。none表示让gcc自动猜测。
-M 生成文件关联的信息,包含目标文件所依赖的所有源代码。
8. GCC编译静态库和动态库

GCC 工具链中,静态库与动态库的构建本质上都依赖于目标文件(*.o),区别在于链接阶段的组织方式。静态库通过 ar 将多个目标文件归档为 libxxx.a,本质是简单的文件打包;链接时,ld 会将所需符号直接拷贝进最终可执行文件,因此生成的程序不再依赖外部库文件。这种方式有利于部署独立可执行程序,但会增加体积,也不利于统一升级。

通常编译形式如下:

shell 复制代码
gcc -c foo.c            #生成 foo.o目标文件
ar rcs libfoo.a foo.o   #生成 libfoo.a 静态库 

使用如下(加上了-static选项,一个使用搜索路径,一个直接指定文件)。:

shell 复制代码
gcc hello.c -static libfoo.a -o hello 
gcc hello.c -static -L. -lfoo -o hello

动态库(libxxx.so)则以共享对象形式存在,使用 -shared 生成,并配合 -fPIC 产生位置无关代码。PIC 的核心是通过间接寻址访问全局符号,使代码在任意加载地址均可正确执行,从而支持多个进程共享同一份物理内存页。在现代 Linux 系统中,默认采用 ELF 格式,动态链接依赖 ld-linux.so 进行符号解析和重定位。

动态库的编译方式基本如下:

shell 复制代码
gcc foo.c -shared -fPIC -o libfoo.so

使用-fPIC生成位置无关代码。

使用如下:无需加上-static选项

shell 复制代码
gcc hello.c libfoo.so -o hello
gcc hello.c -L. -lfoo -o hello

静态链接与动态链接在行为上存在显著差异,如下所示:

维度 静态库 *.a 动态库 *.so
链接时机 编译期完成 运行期解析
文件体积 较大 较小
部署复杂度 需保证运行环境
升级影响 需重新编译 可独立替换

链接动态库时若未指定 -static,编译器默认优先查找共享库。运行失败通常源于动态加载路径问题。系统加载顺序一般为:rpathLD_LIBRARY_PATH/etc/ld.so.cache → 默认路径(如 /lib/usr/lib)。可通过 ldd 查看依赖关系:

shell 复制代码
ldd hello

若输出显示 "not found",说明动态库未被定位。

在工程实践中,更推荐使用 rpathRUNPATH 机制嵌入路径,而非长期依赖环境变量。链接阶段可通过:

shell 复制代码
gcc hello.c -L. -lfoo -Wl,-rpath,'$ORIGIN' -o hello

其中 $ORIGIN 表示可执行文件所在目录,适用于可移植部署场景。动态加载流程可概括如下:
Filesystem ld.so.cache ld-linux Executable Filesystem ld.so.cache ld-linux Executable 启动并请求依赖库 查询缓存 按 rpath / 环境变量 搜索 完成重定位与符号绑定

理解静态与动态链接的差异,有助于在性能、部署和可维护性之间取得平衡。在嵌入式或发布独立工具时,静态链接更具确定性;而在大型系统或插件化架构中,动态库更利于模块化与版本管理。

Once Day

也信美人终作土,不堪幽梦太匆匆......
如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注!
(。◕‿◕。)感谢您的阅读与支持~~~

相关推荐
爱编码的小八嘎2 小时前
第2章 认识CPU-2.2 16位微处理器(2)
c语言
01二进制代码漫游日记4 小时前
自定义类型:联合和枚举(一)
c语言·开发语言·学习·算法
二年级程序员5 小时前
单链表算法思路详解(下)
c语言·数据结构·算法
Sunsets_Red5 小时前
浅谈随机化与模拟退火
java·c语言·c++·python·算法·c#·信息学竞赛
白太岁5 小时前
操作系统开发:(9) 从硬件复位到程序执行:如何编写符合硬件动作的启动文件与链接脚本
c语言·汇编·嵌入式硬件·系统架构
麦德泽特8 小时前
机器人赛事系统架构:基于UDT和MQTT的低延迟、高可靠通信
c语言·开发语言·安全·系统架构·机器人
进击的横打9 小时前
【车载开发系列】浮点数与整型数的转换
c语言·车载系统
麦德泽特11 小时前
蓝牙与WiFi之外:为机器人选择合适的近距离无线通信技术
c语言·开发语言·安全·机器人·ssh
我 see your eyes13 小时前
CLA_TASK 任务的理解
c语言·c++·dsp开发