ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux系统 利用g++进行C++的多文件编译运行

2022-01-23 19:00:59  阅读:465  来源: 互联网

标签:文件 ++ C++ 编译 gdb cpp Linux test


Linux系统下大家的编译器环境应该都是安装的gcc编译器,调试器是gdb,我们可以通过gcc编译器对C++文件的编译过程,对源代码的编译过程有一个更好的了解,这有助于我们自己编写一些vscode的js文件。

1.利用gcc编译器进行C++多文件编译和运行


首先我们要了解gcc的编译过程。

1.1 gcc编译过程


gcc编译过程分为4步:预处理、编译、汇编、链接
下面以一个简单的输出文件test.cpp来演示gcc的这四步编译过程。
test.cpp代码如下:

#include <iostream>
using namespace std;
int main()
{
        cout << "This my first cpp programer in Linux" << endl;
        return 0;
}

1.预处理-Pre-Procession
预处理生成的是.i文件,实际上就是将include的头文件展开。

# -E 选项指示编译器对输入文件进行预处理
g++ -E test.cpp -o test.i

预处理文件的最后结果如下:
image
我们可以看到文件比原来大了好多,预处理将头文件iostream展开,其他部分基本不变。

2.编译-Compiling
编译生成.s文件,编译过程是将C代码转换成汇编语言。

# -S   编译选项告诉 g++ 为  C++  代码产生汇编语言后停止编译
# g++ 产生的汇编语言文件的缺省扩展名是  .s
g++ -S test.i -o test.s

产生的.s文件的部分结果如下:
image
可以看多很多汇编指令。

3.汇编-Assembling
汇编是将编译生成的.s文件转换为二进制语言,生成一个.o文件。

# -c   编译选项告诉 g++ 为  C++  代码编译为机器语言停止编译
# 缺省时 g++ 建立的目标代码有一个  .o的扩展名
g++ -c test.s -o test.o

test.o文件部分结果如如下:
image
可以看到是乱码,这是因为vim不识别机器语言导致的。

4.链接-Linking
链接需要的其他文件后生成可执行文件。

# -o 编译选项来为将产生的可执行文件用指定的文件名
g++ test.o -o test

运行可执行文件,使用./test来运行,结果如下:
image

1.2 g++重要编译参数


1.-g 编译带调试信息的可执行文件

# -g 选项告诉 GCC 产生能被 GNU 调试器使用的调试信息,以调试程序。

# 产生带调试信息的可执行文件test
g++ -g test.cpp -o test

2.-O[n] 优化源代码

## 所谓优化,例如省略掉代码中从未使用的变量、直接将常量表达式用结果值代替等等,这些操作会所见所包含的代码量,提高最终生成的可执行文件的运行效率

# -O 选项告诉 g++ 对源代码进行基本的优化。这些优化在大多数情况下都会是程序执行的更快。 -O 选项告诉g++产生尽可能小和尽可能快的代码。

# -O 同时减小代码的长度和执行时间,效果等价与-O1
# -O0 表示不做优化
# -O1 为默认优化
# -O2 完成-O1的优化之后,进行一些额外的调整工作,如指令调整等
# -O3 包括循环展开和其他一些与处理特性相关的优化工作
# 选项将使编译的速度比 -O 时慢,但通常产生的代码执行速度更快。

# 使用 -O2优化源代码,并输出可执行文件
g++ -O2 test.cpp -o test_with_O2

3.-l和 -L 指定库文件 | 指定库文件路径

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

# 链接glog库
g++ -lglog test.cpp

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

# 链接mytest库,libmytest.so在/home/bing/mytestlibfolder目录下
g++ -L/home/bing/mytestlibfolder -l libmytest test.cpp

4.-I 指定头文件搜索目录

# -I
# /usr/include目录一般不用指定,gcc知道去那里找,但是如果头文件不在/usr/include里我们就要用-I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上-I/myinclude参数了,不加会得到一个“xxxx.h:No such file or directory”的错误,-I也可以用相对路径来指定。

g++ -I/myinclude test.cpp
  1. -Wall 打印警告信息
  2. -w 关闭警告信息
  3. -std=c++11 设置编译标准
  4. -o指定输出文件名 缺省会输出一个a.out的文件
  5. -D 定义宏
# 在使用gcc/g++编译的时候定义宏

# 常用场景
# -DDEBUG 定义DEBUG宏,可能文件中有DEBUG宏的相关信息,用个DDEBUG来选择开启或关闭DEBUG

样例如下:

// -Dname 定义宏name,默认定义内容为字符串“1”

# include <stdio.h>
{
	using std::cout;
	#ifdef DEBUG
		printf("DEBUG LOG\n");
	#endif
		printf("in\n");
}

1.3 使用g++命令行编译

使用g++编译器对目录结构如下的多文件程序进行编译。
image
三个文件的内容如下:

// include file and definition of prototype
#include <iostream>
void Swap(int &a, int &b);
// definition of function
#include "Swap.h"
void Swap(int &a, int &b)
{
        int temp;
        temp = a;
        a = b;
        b = temp;
}
// main function
#include "Swap.h"

int main()
{
        using namespace std;
        int a = 10;
        int b = 20;
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        Swap(a,b);
        cout << "After Swap(a,b)" << endl;
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        return 0;
}

1.3.1 直接编译


在终端输入如下指令,

g++ main.cpp ./src/Swap.cpp -Iinclude

会产生一个 a.out的可执行文件,执行结果如下:
image

1.3.2 生成库文件并编译


  • 链接静态库生成可执行文件:
## 进入src目录下
cd src

# 汇编,生成Swap.o文件 -c和-I的顺序可以颠倒
g++ Swap.cpp -c -I../include
# 生成静态库libSwap.a
ar rs libSwap.a Swap.o

## 回到上级目录
cd ..

# 链接,生成可执行文件:staticmain
g++ main.cpp -Iinclude -lSwap -Lsrc -o staticmain

通过./staticmain运行该文件,运行结果如下:
image

  • 链接动态库生成可执行文件:
## 进入src目录下
cd src

# 生成动态库libSwap.so
g++ Swap.cpp -I../include -fPIC -shared -o libSwap.so
## 上面的命令等价于以下两条命令
# g++ Swap.cpp -I../include -c -fPIC
# gcc -shared -o libSwap.so Swap.o

## 回到上级目录
cd ..

# 链接,生成可执行文件:dyna_main
g++ main.cpp -Iinclude -lSwap -Lsrc -o dyna_main

输入./dyna_main,来运行该文件,会提示运行错误:
./dyna_main: error while loading shared libraries: libSwap.so: cannot open shared object file: No such file or directory
出现该错误的原因是需要添加libSwap.so的路径,通过以下命令运行:
LD_LIBRARY_PATH=src ./dyna_main
运行结果如下:
image

2. 使用gdb调试器对代码进行调试

GDB介绍:

  • GDB(GNU Debugger)是一个用来调试C/C++程序的功能强大的调试器,是Linux系统开发C/C++最常用的调试器。
  • 程序员可以使用GDB来跟踪程序中的错误,从而减少程序员的工作量。
  • Vscode是通过GDB调试其来实现C/C++调试工作的。

GDB主要功能:

  • 设置断点(断点可以是条件表达式)
  • 使程序在指定代码上暂停执行,便于观察
  • 单步执行程序,便于调试
  • 查看程序中变量值的变化
  • 动态改变程序的执行环境
  • 分析崩溃程序产生的core文件

4.1 常用调试命令参数

调试执行: 执行gdb[exefilename],进入gdb调试程序,其中exefilename为要调试的可执行文件名

## 以下命令后括号内为命令的简化使用,比如rur(r),输入r就代表run

(gdb)help(h) 	# 查看命令帮助, 在gdb中输入help + 命令

(gdb)run(r)	# 重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件)

(gdb)start	# 单步执行,运行程序,停在第一行执行语句

(gdb)list(l)	# 查看源代码(list-n,从第n行开始查看代码。list + 函数名:查看具体函数)

(gdb)set	# 设置变量的值

(gdb)next(n)	# 单步调试(逐过程,函数直接执行)

(gdb)step(s)	# 单步调试(逐语句:跳入自定义函数内部执行)

(gdb)backtrace(bt) # 查看函数的调用的栈帧和层级关系

(gdb)frame(f)	# 切换函数的栈帧

(gdb)info(i)	#查看函数内部变量的数值

(gdb)finish	# 结束当前函数,返回到函数调用点

(gdb)continue(c) # 继续运行

(gdb)print(p)	#打印值及地址

(gdb)quit(q)	# 退出gdb
(gdb)break+num(b)	# 在num行设置断点

(gdb)info breakpoints	# 查看当前设置的所有断点

(gdb)delete breakpoints num(d)	#删除第num个断点

(gdb)display	# 追踪查看具体变量值

(gdb)undisplay	# 取消追踪观察变量

(gdb)watch	# 被设置观察点的变量发生修改时,打印显示

(gdb)i watch	# 显示观察点

(gdb)enable breakpoints	# 启用断点

(gdb)disable breakpoints	# 禁用断点

(gdb)x	# 查看内存x/20xw 显示20个单元,16进制,4字节没单元

(gdb)run argv[1] argv[2]	# 调试时命令行传参

(gdb)set follow-fork-mode child	# Makefile项目管理:选择跟踪父子进程(fork())

Tips:
1.编译程序时加上-g,之后才能用gdb -g main.c -o main
2.回车键:重复上一命令

标签:文件,++,C++,编译,gdb,cpp,Linux,test
来源: https://www.cnblogs.com/Fight-go/p/15836109.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有