Linux 编程环境

Linux 编程环境

Vim

Vim

GCC编译器

GCC(GNU Compiler Collection)是一款编译语言编译器,此项目最早由GNU计划的发起者理查德· 斯托曼开始实施。第一版GCC于1987年发行,最初的GCC代表GNU C Compiler,即GNU的C语言编译器。后来经过不断地发展,GCC适应了C++、Objective-C、Java、Go等更多编译语言。GCC最重要的特点为实现跨硬件平台编译,即可在当前的CPU平台上为其他体系结构的硬件平台(ARM、MIPS、X86、PowerPC)开发软件,目前这一方式被广泛应用于嵌入式开发中。

GCC编译器的工作目的就是将开发者编写的语言代码变成可以被机器识别的二进制码。

一个完整的编译器主要由以下4部分组成,它们可称为编译器的主要组件。
(1)分析器:将源程序代码转换为汇编语言。
(2)汇编器:将汇编语言的代码转换为CPU可以执行的字节码。
(3)链接器:将汇编器生成的单独的目标文件组合成可执行的应用程序。
(4)标准C库:提供对核心函数的支持,如果应用程序使用到C库中的函数,C库就会通过链接器与源代码连接,来生成最终的可执行程序。

  • gcc(GUN C Compiler)是GCC中的c编译器
  • g++(GUN C++ Compiler)是GCC中的c++编译器。

gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。

GCC 编译器是 Linux 下默认的 C/C++ 编译器,大部分 Linux 发行版中都是默认安装的。若需要安装,在终端中执行以下命令安装。

sudo apt install build-essential	# 安装
gcc --version						# 查看版本

也可以安装其他组件
sudo apt install g++
sudo apt install gfortran
gcc hello.c -o hello 		# 编译生成 hello 可执行文件
./hello						# 运行可执行文件	

上述命令实际上是多个步骤的结合,依次为:
gcc hello.c -E -o hello.i	# 预处理(Pre-Processing),生成预编译文件 .i
gcc hello.i -S -o hello.s	# 编译(Compiling),生成汇编文件 .s
gcc hello.s -c -o hello.o	# 汇编(Assembling),生成目标文件(二进制文件).o
gcc hello.o -o hello.out	# 链接(Linking),生成可执行程序 .exe / .out

GCC编译流程

  1. 预处理
    在预处理阶段GCC主要处理带“#”的指令,如#include(头文件)、#define(宏定义)等,并删除注释、添加行号和文件名标识。例如,例中的代码,在预处理的阶段将把包含的头文件stdio.h添加进来(解析头文件),然后生成预处理文件test.i。
    预处理可以通过GCC编译器单步编译实现,只需在命令gcc中添加选项“-E”即可。该选项的作用就是让编译器执行完预处理后,生成预处理文件,停止编译过程。
    其中“-o”表示指定生成的新文件的名称。

  2. 编译
    编译阶段中,GCC对预处理文件进行词法分析、语法分析、语义分析,检查代码的规范性。确认无误后,GCC将代码翻译为汇编语言。同样,编译也可以使用GCC编译器进行单步操作。添加选项“-S”即可完成编译操作,生成汇编文件,而不会继续执行汇编处理。

  3. 汇编
    汇编阶段汇编代码转换为机器可以执行的指令。使用编译器进行单步操作,通过添加选项“-c”即可指定生成二进制的目标文件。例中生成二进制目标文件 test.o。

  4. 链接
    链接是一个复杂的过程,包括符号地址确定、符号解析与重定位、指令修正等。链接阶段有一项重要的工作,就是链接库文件,程序代码中经常会出现一些函数接口的使用,这些函数并不需要开发者自己实现,其功能已经被写好并编译到函数库中,开发者只需要调用库函数即可。
    函数库分为静态库与动态库两种。对静态库而言,编译链接时会把库文件代码加载到执行文件中,因此生成的文件体积较大,但运行时不需要库文件。动态库则刚好相反,在编译链接时并不会将库文件加载到执行文件,而是在程序执行时加载库文件。
    当需要获取特定的编译文件时,可以考虑单步执行编译处理,也可以一次性执行多个步骤,如例所示,将源程序代码直接编译生成二进制的目标文件,本次编译经历预处理、编译、汇编3个阶段。读者也可以根据情况,灵活执行编译处理。

gcc编译器版本切换

gcc -v 	# 查看版本
g++ -v 	# 查看版本
sudo apt-get install gcc-12 	# 安装
sudo apt-get install g++-12 	# 安装
ls /usr/bin/gcc*				# 查看当前系统中编译器版本
ls /usr/bin/g++*

# 设置编译器的优先级
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100

# 切换编译器的版本
sudo update-alternatives --config gcc
# 示例如下
root@ubuntu:~# sudo update-alternatives --config gcc
There are 2 choices for the alternative gcc (providing /usr/bin/gcc).

  Selection    Path             Priority   Status
------------------------------------------------------------
  0            /usr/bin/gcc-11   100       auto mode
* 1            /usr/bin/gcc-11   100       manual mode
  2            /usr/bin/gcc-12   100       manual mode
Press <enter> to keep the current choice[*], or type selection number: 2
update-alternatives: using /usr/bin/gcc-12 to provide /usr/bin/gcc (gcc) in manual mode
root@ubuntu:~# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper

# 切换编译器的版本
sudo update-alternatives --config g++

GDB调试器

GDB,全称GNU调试器(GNU Debugger),是一个强大的Unix系统下的源代码级调试工具。它可以帮助程序员查看程序在执行过程中的内部状态,从而更好地理解程序的运行机制。GDB主要用于调试C和C++语言编写的程序,可以帮助用户完成查看程序的内部结构、查看自定义程序的启动方式、设置条件断点、单步调试源代码等各种调试工作。它的存在,使得我们能够更深入地了解程序的运行过程,找出并修复程序中的错误。

  • 准备这样一份 hello.c
#include<stdio.h>
void swap(int a, int b) {
    
    
    int t = a;
    a = b;
    b = t;
    printf(" swap : a = %d, b = %d \n", a, b);
}

int main(int argc, char* argv[]) {
    
    
    int a = 10, b = 20;
    printf("start : a = %d, b = %d \n", a, b);
    swap(a, b);
    printf("  end : a = %d, b = %d \n", a, b);
    return 0;
}
  • 常用的一些命令
sudo apt-get install gdb 	# 安装 gdb
gdb -v 						# 查看版本

gcc hello.c -g -o hello	 	# 生成可调式的可执行文件
gdb hello		# 执行调试,进入调试界面
list 1 			# 从第 1 行开始查看调试文件
list			# 继续查看 
break [line] 	# 设置断点,还有其他方式
info break [n] 	# 长查看断点 [n] 为断点号
r				# 运行代码
p + var			# 查看当前 变量 var 的值
continue		# 恢复程序运行
step/next [count]	# 单步执行,count条指令
quit			# 退出gdb模式
kill			#停止程序执行

【Linux】GDB保姆级调试指南(什么是GDB?GDB如何使用?)

Make工程管理器

make是linux中的自动化构建工具,一般来说系统会自带make,如果没有,安装即可。

sudo apt install make 	# Debian系安装make

make是个命令,也是个可执行程序,用来解析Makefile 文件的命令,这个命令存放在/usr/bin/目录下。

Makefile 是描述程序的编译规则的文本文件,执行 make 命令的时候,make 命令会在当前目录下寻找 Makefile 文件,根据Makefile文件里的规则,编译程序。

Makefile的编写语法规则

 目标:依赖文件列表
        命令列表
  • 目标:要产生的文件名称,目标可以是可执行文件或其他object文件
  • 依赖文件列表:由多个依赖文件组成,每个依赖文件是用来输入而产生目标的文件,一个目标通常有0个或多个依赖文件
  • 命令列表:由0个或多个命令组成,代表 make 执行的动作,注意一个规则可以包含多个命令,存在多个命令时,每个命令占一行

Makefile文件内容举例:

  • 使用 vim Makefile,并输入以下信息
hello:hello.o
    gcc -o hello hello.c
clean:
    rm hello 
    @rm *.o

hello 可执行文件的生成需要依赖 hello.o 文件,通过 gcc -o hello hello.c 生成相应文件。

clean 是清空命令,可以用来删除相应的信息。

@ 符号的使用可以取消回显。

root@ubuntu:~$ ls
hello.c  Makefile
root@ubuntu:~$ cat Makefile 
hello:hello.o
        gcc -o hello hello.c
clean:
        rm hello
        @rm *.o
root@ubuntu:~$ make		# 利用make自动构建项目
cc    -c -o hello.o hello.c
gcc -o hello hello.c
root@ubuntu:~$ ls
hello  hello.c  hello.o  Makefile
root@ubuntu:~$ ./hello
Hello World
root@ubuntu:~$ make clean  # 项目清理
rm hello
root@ubuntu:~$ ls
hello.c  Makefile
root@ubuntu:~$ 

多个源文件的项目构建样例

  • main.c
#include<stdio.h>
#include "func.h"

int main(int argc, char* argv[]){
    
    
    func();
    return 0;
}
  • func.c
#include<stdio.h>

void func(){
    
    
    puts("Hello World");
}
  • func.h
#ifndef _FUNC_H
#define FUNC_H

void func();

#endif
  • Makefile
main : main.o func.o                    # 目标:依赖
        gcc main.o func.o -o main       # 指定依赖如何生成目标
main.o : main.c
        gcc -c main.c -o main.o         # .o文件依赖于.c文件
func.o : func.c
        gcc -c func.c -o func.o
.PHONY:clean    # 伪目标,避免出现符号clean与文件clean重名的情况
clean:
        rm *.o main

  • 执行
make		# 执行编译
make clean	# 清除处理,删除目标文件
  • Makefile 使用自动变量进行替换
main : main.o func.o
        gcc $^ -o $@
main.o : main.c
        gcc -c $< -o $@
func.o : func.c
        gcc -c $< -o $@
.PHONY:clean
clean:
        rm *.o main
  • Makefile 自定义变量
CC = gcc
OBJS = main.o func.o
CFLAGS = -Wall -O2 -g
OBJ = main      # 定义变量(无类型),并赋值

$(OBJ) : $(OBJS)
        $(CC) $^ -o $@
#$(OBJ).o : $(OBJS).c
#        $(CC) $(CFLAGS) -c $< -o $@
#func.o : func.c
#        $(CC) $(CFLAGS) -c $< -o $@
.PHONY:clean
clean:
        rm -f *.o $(OBJ)

Makefile 的规则

  1. 编译C程序的隐含规则:.o 的依赖自动推导为 .c
  2. 链接目标文件的隐含规则: 可执行文件的依赖自动推导为 .o
CC = gcc
OBJS = main.o func.o
CFLAGS = -Wall -O2 -g
OBJ = main      # 定义变量(无类型),并赋值

$(OBJ) : $(OBJS)

.PHONY:clean
clean:
        rm -f *.o $(OBJ)
  1. VPATH 的使用
    如果在当前目录没有找到对应的文件,会到 PATH 指定的目录下寻找。
CC = gcc
OBJS = main.o func.o
CFLAGS = -Wall -O2 -g
OBJ = main
VPATH = ./test ./func

$(OBJ) : $(OBJS)

.PHONY:clean
clean:
        rm -f *.o $(OBJ)