g++/gcc
gcc
和 g++
分别是 GNU C 编译器和 GNU C++ 编译器,常用于编译 C 和 C++ 程序。以下为你详细介绍它们的使用方法:
基本使用
1. 编译单个 C 文件
使用 gcc
编译该文件:
gcc hello.c -o hello
编译成功后,会生成一个名为 hello
的可执行文件,运行该文件:
./hello
就会输出 Hello, World!
。
2. 编译单个 C++ 文件
使用 g++
编译该文件:
g++ hello.cpp -o hello
编译多个源文件
编译多个源文件的多种情况:
多个相关联的c和c++文件 ->生成一个可执行文件 (extern + 一次-o )
多个不关联的c和c++文件 -> 分别生成多个可执行文件 (多次-o)
多个类型相同的(c/c++)文件:分别生成多个/一个可执行文件 (extern + 多次(分别g++/gcc)/一次-o )
1. 编译多个 C 源文件
假设你有两个 C 源文件 main.c
和 func.c
,main.c。
使用 gcc
编译这两个文件:
gcc main.c func.c -o main
上述命令会将 main.c
和 func.c
一起编译...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
extern的使用
上面的的文件main.c里面有用到一个关键字:extern
1,声明外部变量
当你在一个源文件中需要使用另一个源文件中定义的全局变量时,可以使用 extern
关键字来声明该变量。
示例代码:
#include <stdio.h>
// 定义全局变量
int global_variable = 10;
void print_variable();
int main() {
print_variable();
return 0;
}
#include <stdio.h>
// 声明外部变量
extern int global_variable;
//这里就是使用了上面的文件的global_variable变量。
void print_variable() {
printf("The value of global_variable is: %d\n", global_variable);
}
在第二个文件 中,使用 extern int global_variable;
声明了一个外部变量 global_variable
,表示该变量的定义在其他文件中。这样,file2.c
就可以使用 file1.c
中定义的 global_variable
变量了。
2,声明外部函数
当你在一个源文件中需要调用另一个源文件中定义的函数时,可以使用 extern
关键字来声明该函数。不过,在 C 语言中,函数声明时 extern
关键字通常可以省略。
// 声明外部函数:和声明外部变量一样:
//extern + 函数/变量类型 + 变量名/函数名();
extern void print_message();
void call_print_message() {
// 调用外部函数
print_message();
}
在 file2.c
中,使用 extern void print_message();
声明了一个外部函数 print_message
,表示该函数的定义在其他文件中。
C++ 中 extern
声明外部变量和函数的用法与 C 语言类似。
extern + 函数/变量类型 + 变量名/函数名();
3.extern "C"
的使用
在 C++ 中,由于函数名会进行名称修饰(Name Mangling),以支持函数重载等特性,而 C 语言没有名称修饰。当需要在 C++ 代码中调用 C 语言编写的函数,或者在 C++ 代码中导出 C 风格的接口时,就需要使用 extern "C"
。
extern "C" void c_function();
在 上面的C++文件中,使用 extern "C"
声明了一个 C 语言函数 c_function
,这样 C++ 编译器就会以 C 语言的方式处理该函数的名称,避免名称修饰带来的链接问题。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
可以使用一条指令同时分别编译无关联的 C 文件和 C++ 文件
使用 g++
编译器(输入两次-o 生成两个可执行文件)
g++ -o test_c test_c.c && g++ -o test_cpp test_cpp.cpp
在这个命令中,&&
是逻辑与运算符,它确保前一个命令(编译 test_c.c
)执行成功后,才会执行后一个命令(编译 test_cpp.cpp
)。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
深入了解g++/gcc
在 Linux 系统中,使用 GCC(GNU Compiler Collection)编译 C 或 C++ 程序时,从源代码到可执行文件的生成一般会经历预处理、编译、汇编和链接四个主要阶段。下面为你详细介绍每个阶段的作用及对应的 Linux 命令。
示例代码
1. 预处理阶段
预处理是编译的第一个阶段,预处理器会根据源文件中的预处理指令(如 #include
、#define
等)对源文件进行处理,处理后的文件通常以 .i
为扩展名。
gcc -E hello.c -o hello.i
参数解释:
-E
:告诉 GCC 仅进行预处理操作,不进行后续的编译、汇编和链接。-o
:指定输出文件的名称,这里将预处理后的结果输出到hello.i
文件中。
2. 编译阶段
编译阶段将预处理后的文件(.i
文件)翻译成汇编代码,生成的汇编文件通常以 .s
为扩展名。
gcc -S hello.i -o hello.s
参数解释:
-S
:告诉 GCC 进行编译操作,生成汇编代码,不进行后续的汇编和链接。-o
:指定输出文件的名称,这里将编译后的汇编代码输出到hello.s
文件中。
3. 汇编阶段
汇编阶段将汇编代码(.s
文件)转换成机器码,生成目标文件,目标文件通常以 .o
为扩展名。
gcc -c hello.s -o hello.o
参数解释:
-c
:告诉 GCC 进行汇编操作,生成目标文件,不进行链接。-o
:指定输出文件的名称,这里将汇编后的目标文件输出到hello.o
文件中。
4. 链接阶段
链接阶段将多个目标文件(.o
文件)和所需的库文件链接在一起,生成可执行文件。
gcc hello.o -o hello
参数解释:
-o
:指定输出文件的名称,这里将链接后的可执行文件输出为hello
。
简化命令
在实际开发中,我们通常不需要分步执行上述命令,GCC 可以直接将源文件编译成可执行文件,只需一条命令即可:
gcc hello.c -o hello
这条命令会依次完成预处理、编译、汇编和链接四个阶段,最终生成可执行文件 hello
。
运行可执行文件
./hello
程序将输出 Hello, World!
。
对于 C++ 文件
如果是 C++ 文件,如 hello.cpp
,上述过程基本相同,只是需要使用 g++
编译器代替 gcc
,例如:
g++ -E hello.cpp -o hello.i # 预处理
g++ -S hello.i -o hello.s # 编译
g++ -c hello.s -o hello.o # 汇编
g++ hello.o -o hello # 链接
g++ hello.cpp -o hello
//简化版
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
动态库和静态库
动态库(Dynamic Library)和静态库(Static Library)是软件开发中用于代码复用和模块化的重要工具,它们在不同场景下有着各自的优势和用途。
静态库
使用场景
- 发布独立应用程序:当你需要发布一个独立的可执行程序,不希望依赖外部的库文件时,可以使用静态库。这样程序在运行时不需要额外的库支持,方便在不同环境中部署。
- 对性能要求较高的场景:静态库在链接时会将库代码直接嵌入到可执行文件中,程序运行时无需进行额外的动态加载操作,因此可以提高程序的运行速度。
创建静态库:
假设我们有两个源文件 add.c
和 sub.c
:
gcc -c add.c sub.c
然后使用 ar
命令将目标文件打包成静态库:
ar rcs libmath.a add.o sub.o
其中,ar
是用于创建和操作静态库的工具,r
表示将目标文件插入到静态库中,c
表示创建静态库,s
表示生成索引。
使用静态库:
创建一个使用静态库(extern静态库内的函数)的源文件 main.c
:
#include <stdio.h>
// 声明函数
extern int add(int a, int b);
extern int sub(int a, int b);
int main() {
int result1 = add(5, 3);
int result2 = sub(5, 3);
printf("5 + 3 = %d\n", result1);
printf("5 - 3 = %d\n", result2);
return 0;
}
使用以下命令编译并链接静态库:
gcc main.c -L. -lmath -o main
其中,-L
选项指定库文件的搜索路径,.
表示当前目录;-l
选项指定要链接的库名,math
表示要链接的库名为 libmath.a
;-o
选项指定生成的可执行文件名为 main
。
运行生成的可执行文件:./main
动态库
使用场景
- 共享代码资源:多个程序可以共享同一个动态库,减少内存占用和磁盘空间的浪费。例如,系统中的许多程序可能会共享同一个标准 C 库。
- 方便库的更新和维护:当动态库的代码发生更新时,只需要替换动态库文件,而不需要重新编译所有依赖该库的程序。
用法
创建动态库:
使用之前的 add.c
和 sub.c
源文件,使用以下命令将它们编译成动态库:
gcc -shared -fPIC add.c sub.c -o libmath.so
其中,-shared
选项表示生成动态库,-fPIC
选项表示生成位置无关代码,-o
选项指定生成的动态库文件名为 libmath.so
。
使用动态库:
创建一个使用动态库的源文件 main.c
,代码与之前使用静态库时的 main.c
相同。
使用以下命令编译并链接动态库:
gcc main.c -L. -lmath -o main
这里的编译和链接命令与使用静态库时相同。
运行生成的可执行文件时,需要告诉系统动态库的搜索路径:
将动态库文件复制到系统默认的库搜索路径(如 /usr/lib
或 /usr/local/lib
)中,然后直接运行可执行文件:./main
综上所述,静态库适用于需要独立运行和对性能要求较高的场景,而动态库适用于共享代码资源和方便库更新维护的场景。
注意:动态库:一旦动态库缺失,使用动态库的所有程序都无法运行,比较节省资源,可执行程序体积小。
静态库:比较浪费资源,可执行程序体积大,不依赖其他库,库丢失也无所谓,程序照样可以运行
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
动态链接和静态链接
静态链接和动态链接是在软件开发中,将目标代码和库文件链接成可执行文件或其他可部署形式的两种不同方式,它们各有特点,适用于不同的场景。
静态链接
- 原理:在编译链接阶段,链接器会将所有需要的目标文件和库文件的代码和数据全部复制到最终的可执行文件中。也就是说,静态链接会把程序所依赖的库函数的代码直接嵌入到可执行文件中,使得可执行文件包含了运行所需的所有代码,在运行时不再需要依赖外部的库文件。
- 优点
- 独立性高:生成的可执行文件可以独立运行,不依赖外部库,方便在不同环境中部署,只要目标环境支持该可执行文件的格式和基本运行要求,就可以直接运行。
- 执行效率略高:由于运行时不需要额外加载外部库,程序启动和执行速度相对较快,在一些对性能要求极高的场景中,这一点可能很重要。
- 缺点
- 可执行文件体积大
- 更新维护困难
动态链接
- 原理:在编译链接时,动态链接器只是在可执行文件中记录了对外部库函数的引用信息,而不是把库函数的代码复制到可执行文件中。在程序运行时,操作系统的动态链接器会在运行时加载所需的动态库,并将程序中的引用与动态库中的实际函数和数据进行绑定,使得程序能够正确调用库函数。
- 优点
- 可执行文件体积小
- 易于更新维护
- 资源共享
- 缺点
- 运行时依赖:程序运行时必须依赖相应的动态库,如果动态库不存在或版本不兼容,程序可能无法正常运行。
- 执行效率略低:由于运行时需要动态加载库和进行地址绑定等操作,程序的启动和执行速度可能会比静态链接的程序略慢。
应用场景
- 静态链接:适用于对独立性和性能要求极高,不希望有外部依赖,或者对可执行文件大小不敏感的场景,如一些嵌入式系统、对启动速度要求极高的实时系统等。
- 动态链接:适用于大多数通用的应用程序开发,尤其是在需要共享代码、频繁更新库的场景下,如操作系统的系统库、大型软件框架等。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcc的-static选项
在使用 gcc
编译器进行编译时,-static
是一个重要的选项,它主要用于控制链接方式
-static
选项用于指定进行静态链接。当使用该选项时,gcc
会将程序所依赖的所有库(包括标准 C 库等)都以静态方式链接到可执行文件中。
使用场景
- 提高程序独立性:如果你需要将程序部署到不同的环境中,而这些环境可能没有安装程序所需的动态库,使用静态链接可以确保程序不依赖外部库,能够独立运行。
- 避免库版本兼容性问题:不同系统上的动态库版本可能不同,这可能会导致程序在某些系统上出现兼容性问题。
使用示例
gcc hello.c -o hello
这种方式会使用动态链接,生成的可执行文件 hello
会依赖系统的动态库(如标准 C 库)。
静态链接编译
gcc -static hello.c -o hello_static
使用 -static
选项后,生成的可执行文件 hello_static
会包含所有所需库的代码,不依赖外部动态库。
潜在问题
- 可执行文件体积增大:由于静态链接会将所有依赖的库代码都包含在可执行文件中,因此生成的可执行文件体积会比动态链接的文件大很多。
- 资源浪费
- 库更新困难
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcc和g++的静态库和动态库安装(ubuntu)
云服务器是默认没有安装 c/c++的标准静态库和动态库的:所以需要自行安装
1. 安装基础编译工具和库
首先要确保系统已经安装了基本的编译工具和开发库,使用以下命令进行安装:
sudo apt update
sudo apt install build-essential
build-essential
这个软件包组包含了 GCC、G++、make 等常用的编译工具以及一些基础的开发库。
2. 安装系统自带的静态库和动态库
安装 C 标准库相关的静态和动态库
Ubuntu 系统中的 C 标准库(GNU C Library,即 glibc
)默认就有动态库,若要安装其静态版本,可使用以下命令:
sudo apt install libc6-dev
该命令会安装 glibc
的开发文件,其中包含了静态库文件(通常以 .a
为扩展名)和动态库文件(以 .so
为扩展名)。
安装 C++ 标准库相关的静态和动态库
同样地,对于 C++ 标准库,使用以下命令来安装其开发文件:
sudo apt install g++-multilib
此命令会安装 C++ 标准库的开发文件,涵盖静态库和动态库。