目录
介绍
首先介绍一下GCC
:是GNU Compiler Collection(GNU 编译器套装)缩写,一个由GNU开发用于Linux系统下编程的编译器。GDB
是GNU开源组织发布的一个强大的UNIX下的程序调试工具。
- gcc是GCC中的GUNC Compiler(C 编译器)
- g++是GCC中的GUN C++ Compiler(C++编译器)
但是并不是说gcc编译c语言,g++编译c++。而是:gcc调用了C compiler,而g++调用了C++ compiler。主要区别在于:
- 对于 .c和.cpp文件,gcc分别当做c和cpp文件编译(c和cpp的语法强度是不一样的)
- 对于 .c和.cpp文件,g++则统一当做cpp文件编译
- 使用g++编译文件时,g++会自动链接标准库STL,而gcc不会自动链接STL
- gcc在编译C文件时,可使用的预定义宏是比较少的
- gcc在编译cpp文件时/g++在编译c文件和cpp文件时(这时候gcc和g++调用的都是cpp文件的编译器),会加入一些额外的宏。
6.在用gcc编译c++文件时,为了能够使用STL,需要加参数 –lstdc++ ,但这并不代表 gcc –lstdc++ 和 g++等价,它们的区别不仅仅是这个。
gcc
gcc编译单个文件
> vim hello.c
#include<stdio.h>
int main(void){
printf("hello,world.\n");
}
> gcc hello.c # 编译,会在当前生成名字为`a.out`的可执行二进制文件
> ./a.out
gcc编译两个文件
> vim main.c
include<stdio.h>
int main(void){
double angle;
printf("main function is exec...\n");
printf("input the angle:");
scanf("%lf",&angle);
sin_value(angle);
}
> vim seconds.c
#include<stdio.h>
#include<math.h>
#define PI 3.141592653
void sin_value(double angle){
printf("seconds fun is exec ...\n");
printf("angle = %f, and sin(angle)=%f\n",angle,sin(PI / 180 * angle));
}
> gcc -c main.c seconds.c # 编译两个源码文件,生成对应的目标文件(结尾为.o)
> gcc -o binary main.o seconds.o -lm # 链接两个目标文件,并生成可执行文件binary
> ./binary # 执行
main function is exec...
input the angle:30
seconds fun is exec ...
angle = 30.000000, and sin(angle)=0.500000
说明:创建了两个源码文件,其中
main.c
文件中函数依赖于seconds.c
文件中sin_value(double angle)
函数。首先是编译过程,通过gcc -c
将源码文件编译成目标文件main.o
和seconds.o
。然后对目标文件进行链接操作,通过gcc -o
将目标文件链接,并生成可执行文件binary
。在链接过程中,由于seconds.c
文件中调用了数学函数库math.h
。需要依赖于外部的函数库。在Linux系统中,gcc默认会使用/lib
和/lib64
的函数库。如果要使用自定义位置的函数库,可通过-L{Path}
来添加;当然可以通过-I{Path}
来指定include文件的目录(这个默认使用的是/usr/include
)。对于使用数学函数库
math.h
可以通过加-lm
来指定,也就是表示要使用libm.so
这个函数库。
make
使用make编译
对于上述gcc编译两个文件例子中,如果每次修改了源码,都需要重新执行编译
和链接
两个过程。如果一旦源码文件很多了,重复操作很繁琐。可以通过make
来一个步骤完成(使用makefile
文件然后使用make
宏编译)。在一些最小安装的Linux系统中,默认是不安装make的(Ubuntu安装make:sudo apt-get install make
)。在目录中创建一个makefile文件,且内容如下:
> vim makefile
main: main.o seconds.o
gcc -o binary main.o seconds.o -lm
clean:
rm -f binary main.o seconds.o
# 注意在每个targets(main,clean)下面一行都有个<tab>.
> make # 编译,也就是执行main的指令,等价于make main
> make clean # 清理,也就是执行clean下的指令。
注意,默认使用的文件名为makefile
,当然可以在make
时候通过添加参数-f
指定文件名,比如Makefile
文件编译可通过:make -f Makefile clean
,并且如果makefile
文件内容很多,导致运行过程中不知道有多少个targets
,可以通过make <tab>
来通过Bash
补全。
makefile语法
makefile
的基本语法规则为:
target: 目标文件1 目标文件2...
gcc -o 可执行文件的名字 目标文件1 目标文件2... 等
在makefile
文件中注释是#
,并且一个文件中可以建立多个target
。make
默认的target是main
。在makefile文件中也可以定义变量,通过 =
来设置,并且使用 ${}
或者 $()
来使用变量。
cmake
当工程量很大的时候,手写makefile
又是一个很繁琐且枯燥的工作。如果换了一个平台,可能又得重新编写makefile
(依赖库环境等不同导致)。如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用make
工具,就得为每一种标准写一次makefile
,这将是一件让人抓狂的工作。因此,就衍生出来了cmake
。其功能主要是通过cmake
侦察工作环境,自动生成一个makefile
文件,以便make
工具使用。
除此之外,也有通过编写脚本去侦测工作环境。比如
configure
或config
,侦测完成后,创建makefile
文件。也有由QT
提供的一个编译打包工具qmake
,编译pro
项目生成makefile
文件。
使用步骤
在linux
平台下使用CMake
生成Makefile
并编译的流程如下:
- 写
cmake
配置文件CMakeLists.txt
。 - 执行命令
cmake PATH
或者ccmake PATH
或者cmake-gui
生成Makefile
(ccmake
和cmake
的区别在于前者提供了一个交互式的界面,而cmake-gui
则是直接使用GUI界面操作)。其中,PATH
是CMakeLists.txt
所在的目录。 - 使用
make
命令进行编译。
CMakeLists.txt文件编写规则
CMake的命令有不同类型,包括脚本命令、项目配置命令和测试命令,具体可以参考官网:CMake 教程 — CMake 3.26.0-rc5 文档
创建一个最简单的文件main.c
,内容如下:
#include <stdio.h>
int main(){
for(int i = 1; i < 100; ++i){
printf("%d\n",i);
}
}
在同一目录下创建CMakeLists.txt
文件,且内容如下:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo_for)
# 指定生成目标
add_executable(Demo main.c)
cmake_minimum_required
:指定运行此配置文件所需的CMake
的最低版本;project
:参数值是Demo_for
,该命令表示项目的名称是Demo_for
。add_executable
:将名为main.c
的源文件编译成一个名称为Demo
的可执行文件。
cmake的使用
> ls
CMakeLists.txt main.c
> cmake . # <=== 制作makefile开始,在当前文件夹下找CMakeLists.txt文件
CMake Deprecation Warning at CMakeLists.txt:2 (cmake_minimum_required):
Compatibility with CMake < 2.8.12 will be removed from a future version of
CMake.
Update the VERSION argument <min> value or use a ...<max> suffix to tell
CMake that the project does not need compatibility with older versions.
-- The C compiler identification is GNU 11.3.0
-- The CXX compiler identification is GNU 11.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /cmake
> ll # 编译完成后新增加内容。
-rw-rw-r-- 1 user user 13785 3月 5 15:00 CMakeCache.txt
drwxrwxr-x 5 user user 4096 3月 5 15:00 CMakeFiles/
-rw-rw-r-- 1 user user 1634 3月 5 15:00 cmake_install.cmake
-rw-rw-r-- 1 user user 150 3月 5 14:55 CMakeLists.txt
-rw-rw-r-- 1 user user 87 3月 5 14:48 main.c
-rw-rw-r-- 1 user user 5065 3月 5 15:00 Makefile
说明,上述内容中,通过
cmake
指令对源程序main.c
以及对应的CMakeLists.txt
生成在该环境下的makefile
,该过程中生成了一些CMakeCache
等文件。其作用如下:
CMakeCache.txt
:CMake
中的缓存变量都会保存在CMakeCache.txt
文件中。CMakeFIles/
:包含由CMake
在配置期间生成的临时文件。cmake_install.cmake
:CMake
脚本处理安装规则,并在安装时使用。在Ubuntu中默认是不安装
cmake
的,安装如下:sudo apt-get install cmake
在使用
cmake
过程中可以指定参数-G
、--build
等,可以查看官网或者使用man cmake
在系统中查看。
ccmake的使用
ccmake
有两种用法:
- 通过直接对
CMakeLists.txt
文件生成makefile
文件。相对于cmake
而言,其提供了一种字符界面的GUI操作面板。更加直观。 - 对
cmake
操作过后的cache
文件进行修改,重新生成makefile
文件。
用法1可以理解为从无到有的生成,而用法2可以理解为修改默认配置文件,类似于修改。
ccmake
包含在cmake-curses-gui
包中,所以安装命令:sudo apt-get install cmake-cures-gui
这里对方案1进行展示
> ls
CMakeLists.txt main.c
> ccmake .
EMPTY CACHE
EMPTY CACHE:
Keys: [enter] Edit an entry [d] Delete an entry
CMake Version 3.22.1
[l] Show log output [c] Configure
[h] Help [q] Quit without generating
[t] Toggle advanced mode (currently off)
> c # <==键盘输入c表示配置
CMake Deprecation Warning at CMakeLists.txt:2 (cmake_minimum_required):
Compatibility with CMake < 2.8.12 will be removed from a future version of
CMake.
Update the VERSION argument <min> value or use a ...<max> suffix to tell
CMake that the project does not need compatibility with older versions.
The C compiler identification is GNU 11.3.0
The CXX compiler identification is GNU 11.3.0
Detecting C compiler ABI info
Detecting C compiler ABI info - done
Check for working C compiler: /usr/bin/cc - skipped
Detecting C compile features
Detecting C compile features - done
Detecting CXX compiler ABI info
Detecting CXX compiler ABI info - done
Check for working CXX compiler: /usr/bin/c++ - skipped
Detecting CXX compile features
Detecting CXX compile features - done
Configuring done
> g #<==生成,这里有个问题,得重复按两下c才会出现[g]选项。
Page 1 of 1
CMAKE_BUILD_TYPE
CMAKE_INSTALL_PREFIX /usr/local
CMAKE_BUILD_TYPE: Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ...
Keys: [enter] Edit an entry [d] Delete an entry
CMake Version 3.22.1
[l] Show log output [c] Configure [g] Generate
[h] Help [q] Quit without generating
[t] Toggle advanced mode (currently off)
注意这里,当点击
c
后会自动配置内容,但是并不会出现[g]
选项。这时候需要重新使用[c]
选项,然后[e]
退出后就会出现[g] Generate
选项。
cmake-gui的使用
这个是一个基于QT的GUI界面。在Ubuntu中安装如下:sudo apt install cmake-qt-gui
,使用的时候直接命令:cmake-gui
提供一个GUI界面。点击操作就行。
扩展内容
库
在C/C++中,项目最终都会分成两个部分内容,一个是头文件( .h )
,一部分是源文件( .cpp)
。 如果要编写好的功能给其他程序使用,通常会把源文件打包形成一个动态链接库文件(.so .a)
文件 。 值得注意的是,头文件一般不会打包到链接库中,因为头文件仅仅只是声明而已。而库是别人写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常 。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)
和 动态库(.so、.dll)
链接库也增加了代码的重用性、提高编码的效率,也可看看成是对源码的一种保护。
在
windows
上库名对应的是.lib
,.dll
。而在linux
上库对应的是.a
,`.so
库包含静态链接库
和动态链接库
两种。
静态链接库
在链接阶段,将源文件中用到的库函数与汇编生成的目标文件.o
合并生成可执行文件。该可执行文件可能会比较大。
这种链接方式的好处是:方便程序移植,因为可执行程序与库函数再无关系,放在如何环境当中都可以执行。
缺点是:文件太大,一个全静态方式生成的简单print文件远大于动态链接生成的一样的可执行文件。(类似于一个是值传递,而另一个则是直接引用了地址而已,用的时候根据地址而加入该功能)。而且如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户。
linux
下的静态库文件是.a
,而windows
的静态库文件是.lib
动态链接库
动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。也就是说程序在运行时由系统动态加载动态库到内存,供程序调用。
导入库
导入依赖库,需要导入两个部分的内容:头文件和源文件。源文件一般已经被打成了.so
文件,所以实际上就是导入头文件和导入.so
文件。
- 导入头文件:头文件一般会放置在一个文件夹include中,可以把这个文件夹拷贝到工程内部,也可以放置在外部磁盘上,只需要指定地址找到它即可。
- 导入源文件(库文件):如果只导入了头文件,而没有到实现文件,那么会抛出异常,比如:xxx未定义之类的错误。因此需要导入对应的源文件(一般是
.so
文件)
库文件在链接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般 Linux 系统把/lib
和/usr/lib
两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。但是,对于处于默认库搜索路径之外的库,就需要将库的位置添加到库的搜索路径之中。
将头文件路径和库文件路径添加到指定的系统环境变量中去,具体如下:
- 使用
gcc
编译时将头文件
路径添加到C_INCLUDE_PATH
系统环境变量中; - 使用
g++
编译时将头文件
路径添加到CPLUS_INCLUDE_PATH
系统环境变量中; - 将
动态连接库
路径添加到LD_LIBRARY_PATH
系统环境变量中; - 将
静态库
路径添加到LIBRARY_PATH
系统变量中。
改变系统变量主要有两种形式,一种是临时改变,另一种是永久改变。临时改变系统变量只需要使用export
命令,重启终端后将恢复至先前状态,如export C_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/myinclude
(在原有基础上添加了/myinclude
目录),而永久改变主要是将export
命令写到系统文件中:如/etc/profile
、/etc/export
、~/.bashrc
或者~/.bash_profile
等等。
~/.bashrc
在每次登陆和每次打开shell
都读取一次,而~/.bash_profile
只在登陆时读取一次。但是对于嵌入式Linux来说,有些文件可能没有,这就需要根据目标机器的情况来设置了。
对于/etc/profile
、/etc/export
文件中加入export
命令,是对所有用户而言的,而~/.bashrc
和~/.bash_profile
是对当前用户起作用。