1.1 C++编译环境搭建

436 阅读8分钟

环境搭建

ubuntu系统

# 编译器、调试器安装 # 
sudo apt update
sudo apt install build-essential gdb

# 检查是否安装成功
gcc --version
g++ --version
gdb --version
# 安装cmake #
sudo apt install cmake
# 检查
cmake --version

centos系统

# 安装编译器 # 
yum install -y gcc g++ gcc-c++ make automake texinfo wget openssl openssl-devel
# 安装cmake # 
wget https://cmake.org/files/v3.16/cmake-3.16.8.tar.gz

# 解压并构建
tar -xf cmake-3.16.8.tar.gz
cd cmake-3.16.8
./configure

# 编译,安装
make && make install

# 构建软连接
ln -s /usr/local/bin/cmake /usr/bin/cmake
# 安装gdb # 
# 先安装termcap
wget https://ftp.gnu.org/gnu/termcap/termcap-1.3.1.tar.gz
tar -xf termcap-1.3.1.tar.gz
cd termcap-1.3.1
./configure
make
make install

# 安装gdb
wget http://mirrors.ustc.edu.cn/gnu/gdb/gdb-7.9.tar.xz
tar -xf gdb-7.9.tar.xz
cd gdb-7.9
./configure
make
make install

# 构建软连接
ln -s /usr/local/bin/gdb /usr/bin/gdb

windows

先安装MinGW

安装包网址: www.mingw-w64.org/downloads/

image.png

安装到指定指定目录后,如我是安装在D:\cppCompile\下:

image.png

其中的bin文件夹里面就存放了g++.exe和gcc.exe以及gdb.exe三个可执行文件,我们将待编译(调试)的文件以及相关参数配置输入给这些可执行文件即可以进行编译。 然后我们需要配置环境变量:

我的电脑->右键属性->高级系统设置->环境变量->系统变量->找到Path变量->编辑->新建一个路径->粘贴上MinGW的bin目录地址。

1663296412812.png

打开cmd验证安装是否成功:

gcc.exe --version # 或gcc --version
g++.exe --version # 或g++ --version
gdb.exe --version # 或gdb --version

安装Cmake

这个网站下载cmake比较快

cmake.org/files/

进入LatestRelease目录,找到windows的x86_64版本下载安装包(.msi后缀),下载到指定目录,然后双击文件安装时记得勾选自动添加环境变量。

1663481285402.png

image.png

打开cmd验证安装是否成功:

cmake --version

c++编译过程

1.预处理

指定-E选项表示仅仅对源码文件进行预处理,生成.i文件

以例1的文件示范,仅仅对main.cpp执行预处理

g++ -E  main.cpp -o main.i # -E命令不支持操作多个文件, win和linux都是这个命令

生成的main.i文件的部分内容如下,仍然是可供人直接阅读的:

image.png

2.编译

指定-S选项表示产生汇编语言后停止编译,生成.s文件

g++ -S  main.i  -o main.s # 由main.i生成main.s
g++ -S  main.cpp -o main.s # 由main.cpp生成main.s

main.s是汇编文件,部分内容如下:

image.png

3.汇编

指定-c选项表示产生把源代码文件编译为机器语言

g++ -c  main.s  -o main.o # 由main.s生成main.o
g++ -c  main.cpp  -o main.o # 由main.cpp生成main.o

4.链接

指定-o选项产生可执行bin文件

g++  main.cpp -o main.exe # 由main.cpp生成main.exe

C++编译的重要参数

-g参数

指定该参数可生成带调试信息的的可执行文件

g++ -g main.cpp -o main.exe

-O[n]

指定该参参数可优化源代码

g++ -O2 -g main.cpp -o main.exe

# -O选项:对源代码做基本的优化,效果等价与-O1
# -O0选项:不做优化
# -O1选项:等价-O
# -O2选项:除了完成-O1的优化后还会指定一些额外的优化工作
# -O3选项:优化循环展开以及更多的优化工作

-l和-L

-l指定库文件,-L指定库文件路径

# -l参数(小写的L)就是用来指定程序要链接的库,-l参数紧接着就是库名
# 在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接

# 链接上述三个默认库路径下的glog库
g++ -lglog test.cpp

# 如果要链接的库文件没在上面三个目录里,需要使用-L参数(大写)指定库文件所在目录
# -L参数跟着的是库文件所在的目录名

# 链接/home/bing/myfolder目录下的mytest库,库文件名称为libmytest.so在
# 在写库文件名称的时候省略lib
g++ -L/home/bing/myfolder -lmytest main.cpp

-I

-I指定头文件搜索目录
# /usr/include目录一般是不用指定的,gcc知道去那里找
# 但是如果头文件不在/usr/icnclude里我们就要用-I参数指定了,-I参数可以用相对路径,比如头文件在当前目录,可以用-I.来指定
g++ main.cpp -Iinclude -o main.exe

-Wall

输出警告信息

g++ -Wall main.cpp -o main.exe

-w

关闭输出警告信息 g++ -w main.cpp -o main.exe

-std=c++11

设置编译标准 g++ -std=c++11 main.cpp -o main.exe

-o

指定输出文件名

g++ -std=c++11 main.cpp -o main.exe win中有exe后缀

g++ -std=c++11 main.cpp -o main. linux中无exe后缀

-D

定义宏

c++命令行编译

例1:简单程序

我们先来看一个简单的小例子,在win下将一个简单的HelloWorld程序编译为可执行文件,工程HELLO文件夹下有一个main.cpp文件(先忽略.vscode目录,原生的命令行编译用不到这些文件)

image.png

main.cpp内容如下:

#include<iostream>
using namespace std;

int main(void)
{
    std::cout << "Hello World!" << std::endl;
    return 0;
}

编译过程如下:

# win下, 进入cmd, 切换到main.cpp所在的目录 # 
g++ main.cpp -o main.exe # 编译
main.exe # 执行

# linux下, 切换到main.cpp所在的目录 # 
g++ main.cpp -o main
./main # 执行

可以发现编译结束后,当前目录出现一个可执行文件(win下带有后缀exe,linux下无后缀)

例2:稍微复杂点的多文件工程

linux下,工程目录文件名为Prj1,其目录树结构为:

image.png

main.cpp为主函数,include目录下存放swap.h的头文件,src目录下存放swap.cpp源码文件

  • 主函数main.cpp:
#include "swap.h"

int main(int argc, char **argv){
    swap mySwap(109, 90);  
    mySwap.printInfo();
    mySwap.run();
    mySwap.printInfo();
    return 0;
}
  • 头文件swap.h:
#include <iostream>

class swap
{
public:
    swap(int a, int b)
    {
        this->_a = a;
        this->_b = b;
    }
    void run();
    void printInfo();

private:
    int _a;
    int _b;
};

  • 源码文件swap.cpp:
#pragma once
#include "swap.h"

void swap::run()
{
    int temp;
    temp = this->_a;
    this->_a = this->_b;
    this->_b = temp;
}

void swap::printInfo()
{
    std::cout << "_a:" << this->_a << std::endl;
    std::cout << "_b:" << this->_b << std::endl;
}

编译可执行文件

从上述文件中可以看出,在主函数中我们使用了swap这个类,所以我们需要申明这个类的头文件#include "swap.h",这个头文件存放在include目录下,申明了类的结构和一些方法名称(头文件同时实现了构造方法),而其他的成员函数均定义在src下的swap.cpp源码文件里面,所以我们需要将main.cpp和swap.cpp一起编译为可执行文件,同时告诉编译器头文件去哪个目录下找(-I命令后直接跟上头文件的搜索目录),编译命令如下:

# win下, 进入cmd, 切换到main.cpp所在的目录 # 
g++ main.cpp  src/swap.cpp  -Iinclude -o main.exe # 编译
main.exe # 执行

# linux下, 切换到main.cpp所在的目录 # 
g++ main.cpp  src/swap.cpp  -Iinclude -o main # 编译
./main # 执行

编译静态库与动态库

库文件是别人写好的,我们可以直接使用,但是要遵循相关的规范,这样可以提高我们的代码复用率,类似python中的包,python中直接安装相关包后import导入即可使用,c++的库需要在链接阶段将指定库链接到目标代码中才可以使用。

linux下的静态库/动态库命名方式是:libXXX.alibXXX.so

windows下的静态库/动态库命名方式是:XXX.libXXX.dll

image.png

生成静态库并且链接

在例2的工程中,假设我们想将swap.cpp这个交换两数的功能编译成静态库去供别人使用:

# 切换到src目录
cd src

# 首先汇编
g++ swap.cpp -c -I../include -o swap.o

# 然后生成静态库libswap.a
ar rs libswap.a swap.o

静态库在src下生成,目录结构如下: image.png

然后现在其他开发者需要使用你的这个libswap.a的静态库,只需要写一个main.cpp的源文件,源码中标明你这个库的头文件,将该静态库链接到main.cpp这个目标代码中即可

# 切换到上级目录
cd ..

# 进行链接,-I指定库的头文件目录,-L指定库的目录,-l指定库名称,即可生成staticMain
g++ main.cpp -Iinclude -Lsrc -lswap -o staticMain

# 执行
./staticMain
# [root@localtk CmakeTest]# ./staticMain 
# _a:109
# _b:90
# _a:90
# _b:109

生成动态库并且链接

在例2的工程中,假设我们想将swap.cpp这个交换两数的功能编译成动态库去供别人使用:

# 切换到src目录
cd src

# 生产动态链接库
g++ swap.cpp -I../include -fPIC -shared -o libswap.so

动态库libswap.so在src下生成,目录结构如下:

image.png

# 链接动态库 # 
# 切换到src的上级目录
cd .. 

# 这个链接的命令其实和链接静态库的命令一样
# 那我们如何知道是链接了动态库libswap.so还是链接了静态库libswap.a呢
# 当有名称相同的静态库和动态库存在时,应该是会优先链接动态库
g++ main.cpp -Iinclude -Lsrc -lswap -o dynamicMain

# 执行
./dynamicMain
# [root@localtk CmakeTest]# ./dynamicMain 
# ./dynamicMain: error while loading shared libraries: libswap.so: cannot open shared object file: No such file or directory

# 应该这样执行
LD_LIBRARY_PATH=./src ./dynamicMain
# 出错了,因为是动态链接,libswap.so并没有嵌入到dynamicMain可执行文件中,而是在执行dynamicMain时去系统默认的指定路径下搜索该库,但是该库并不在默认路径下,而是在src下,所以我们需要指定库的搜索目录
# [root@localtk CmakeTest]# LD_LIBRARY_PATH=./src ./dynamicMain 
# _a:109
# _b:90
# _a:90
# _b:109

静态库与动态库的区别

  • 静态库

静态库的链接时间:在编译过程中就被载入到目标程序中

静态库的链接方式:库中的所有函数都被整合到了目标代码

静态库的优点:编译后不依赖外部环境的函数库

静态库的缺点:若使用的静态库发生了更新,必须要重新编译目标程序才能使用更新后的版本,静态库在运行时内存占用大,因为会载入库中的所有函数。

  • 动态库

动态库的链接时间:在编译过程中没有被载入到目标程序中

动态库的链接方式:在目标代码执行时才会去指定位置搜索库文件

动态库的优点:动态库变化不影响目标代码,无须修改目标代码即可使用更新后的库函数,动态库在运行时内存占用小,因为只会载入库中用到的函数模块。

动态库的缺点:依赖外部

参考来源

www.bilibili.com/video/BV1fy…