Linux友人帐之编译器gcc/g++的使用

一、程序的翻译过程

在C语言中,我们已经学过程序的编译和链接,在这里将复习一下我们之前所学的内容并引出后续gcc/g++的内容。

1.1程序的翻译过程

  1. 预处理(头文件展开,去注释,宏替换,条件编译)
  2. 编译:把C变成汇编语言
  3. 汇编:把汇编变成二进制(不是可执行,二进制目标文件不能被执行)
  4. 链接:把你下的代码和C标准库中的代码合起来

1.2理解选项的含义

如果我们直接gcc test.c 就会跳过上述四个过程直接编译生成最终的a.out可执行文件,因此我们不直接这样,而是划分成四条指令依次执行上述的四步翻译过程,在此过程中理解选项的含义。

1.3动态链接和静态链接

库函数调用的地方怎么与标准库关联起来的

a.链接的本质:无非就是我们在调用库函数的时候,与标准库如何关联的这么一个问题!
b.关联的方式有两种:动态链接和静态链接

1.静态链接

链接的时候,不是与标准库产生关联,而是将程序内部要用的方法,给程序拷贝一份,这样就完成了静态链接。

优势:不受库升级或者被删除的影响,这里当然指的是形成可执行二进制程序之后不受删除的影响,如果形成之前被删除的话,拷贝也拷贝不了了。
劣势:形成的可执行程序体积太大,网络、磁盘、内存的资源占用量大,

2.动态链接

通过编译器内部的链接器,来链接标准函数库,值得注意的是,动态链接的时期是在程序运行的时候,如果程序需要链接,链接器就会链接标准函数库

优势:动态链接形成的可执行程序小,在内存、磁盘、网络等方面可以节省资源,与静态链接相比,这是决定性的优势。

  • gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。 gcc hello.o –o hello
  • gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证

 3.理解

事实上,对于动态和静态的理解,就好比在网吧还是家上网一样。如果你在网吧,此时网吧升级就会影响到你,这也就是所谓的动态;如果把网吧的电脑买来带回家上网,说明你已经有一台自己的电脑,相当于拷贝了一份网吧的电脑到自己家,上网就不会受到网吧升级时的影响,这就是所谓的静态。

 因此经过定义与理解的总结:

  • 动态链接: 受库升级或者被删除的影响,形成的可执行程序小,节省资源。

  • 静态链接: 不受库升级或者被删除的影响,形成的可执行程序提交太大! – 网络,磁盘,内存

一、gcc概述

1.1简介

  • GCC:GNU Compiler Collection,GNU编译套件
  • GNU:GNU’s Not Unix
  • GCC是由GNU开发的编程语言编译器,包括C、Cpp、Objective-C、Fortran、Java、Ada、Golang。

1.2gcc命令语法 

  • Linux gcc命令是一个用于编译C语言和其他语言(如C++、Java、Objective-C等)的命令行工具,它是GNU编译器套件(GNU Compiler Collection)的一部分,支持多种操作系统和硬件平台
  • Linux gcc命令的基本语法是:
gcc [选项]... [文件]...

其中,[文件]是指定要编译的源文件,可以是一个或多个。[选项]是用来控制gcc命令的行为和功能的参数,例如:

  • -o <文件>:指定输出的可执行文件或目标文件的名称。
  • -c:只编译不链接,生成目标文件(.o)。
  • -S:只编译不汇编,生成汇编文件(.s)。
  • -E:只进行预处理,生成预处理文件(.i)。
  • -g:生成调试信息,便于使用调试器(如gdb)进行调试。
  • -O <级别>:指定优化级别,从0到3,数字越大表示优化程度越高,也越耗时。
  • -Wall:开启所有的警告信息,有助于发现潜在的错误或不规范的代码。

1.3gcc编译流程分析

1. 编写代码:创建test.c ,并写入(此时代码包含错误):

 2.预处理

输入:编写的文件*.c

输出:中间文件*.i

命令:

gcc -E test.c -o test.i

提示错误

 修正

预处理具体过程:头文件拷贝,去注释,条件编译,宏替换
-E让程序翻译到预处理阶段就停下来,-o指明形成的临时文件名称。

3.编译阶段

输入:中间文件*.i

输出:汇编语言文件*.s

功能:检查语法错误

命令:

gcc -S test.i -o test.s

提示错误

 4.汇编阶段

输入:汇编语言文件*.s

输出:二进制机器码*.o

命令:

gcc -c test.s -o test.o

选项-c:使编译器完成汇编阶段就停止

可以看到我们无法执行这个二进制文件,其实是因为我们的文件中的一些库函数还没有成功的调用,现在仅仅只有这些库函数的声明,这些声明就在拷贝的头文件里面,而库函数的实现在标准库里面,链接阶段会帮助我们将标准库动态链接到我们的程序里面,之后程序才可以正常的运行。

5.链接阶段

输入:二进制机器代码文件*.o

输出:可执行的二进制文件

命令:

gcc test.o -o test

运行./test即可执行程序

一次性编译

gcc [编译文件] -o [目标文件]

gcc test.c -o test

二、编译器gcc的使用

2.1多文件编译

(1)创建一个文件夹

mkdir

(2)创建三个.c文件

(2.1)other1.c

void welcome() {

  printf(“Welcome to the world of Linux\n”);

} 

(2.2)other2.c

int add(int x, int y) {

  return x+y;

}

int sub(int x, int y){

  return x-y;

} 

(2.3)app.c

#include <stdio.h>

void main(){

  int a=15,b=3,c;

  printf(“test in app\n”);

  welcome();

  c = add(a, b);

  printf(“%d + %d = %d\n”, a, b, c); 

  c = sub(a, b);

  printf(“%d - %d = %d\n”, a, b, c);

} 

(3)编译

gcc other1.c other2.c app.c -o app

(4)运行

./app 

2.2增加头文件

(1)增加头文件

(1.1)other1.h

#ifndef OTHER1_H

#define OTHER1_H

#include <stdio.h>

void welcome();

#endif

(1.2)other2.h

#ifndef OTHER2_H

#define OTHER2_H

#include <stdio.h>

int add(int, int);

int sub(int, int);

#endif 

(2)修改other1.c和other2.c

(2.1)other1.c

#include “other1.h”

void welcome() {

  printf(“Welcome to the world of Linux\n”);

} 

(2.2)other2.c

#include “other2.h”

int add(int x, int y) {

  return x+y;

}

int sub(int x, int y){

  return x-y;

} 

(3)修改app.c

#include “other1.h”

#include “other2.h”

void main(){

  int a=15,b=3,c;

  printf(“test in app\n”);

  welcome();

  c = add(a, b);

  printf(“%d + %d = %d\n”, a, b, c); 

  c = sub(a, b);

  printf(“%d - %d = %d\n”, a, b, c);

} 

(4)编译并运行

gcc other1.c other2.c app.c -o app

./app

2.3Linux库的创建与使用

什么是库

静态库的创建和使用

动态库的创建和使用

1. 什么是库?

(1)库:事先已经编译好的代码,经过编译后可以直接调用的文件,本质上来说是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

(2)系统提供的库的路径

/usr/lib

/usr/lib64

(3)Linux库文件名的组成

前缀(lib)+库名+后缀(.a静态库;.so动态库)

libmm.a:库名为mm的静态库;

libnn.so:库名为nn的动态库。

2. 静态库与动态库

载入的顺序是不一样的

(1)静态库的代码在编译时就拷贝到应用程序中,因此当有多个程序同时引用一个静态库函数时,内存中将会调用函数的多个副本。由于是完全拷贝,因此一旦连接成功,静态库就不再需要了,代码体积大。

(2)动态库在程序内留下一个标记,指明当程序执行时,首先必须要载入这些库。在程序开始运行后调用库函数时才被载入,被调用函数在内存中只有一个副本,代码体积小。

2.4静态库的创建和使用

1.静态库的创建步骤

(1)在头文件(.h)中声明静态库所导出的函数

(2)在源文件(.c)中实现静态库所导出的函数

(3)编译源文件,生成目标文件(.o)

(4)通过命令ar将目标文件加入到静态库中

ar rcs [库文件] [目标文件]

(5)将静态库拷贝到系统默认的存放库的路径,或指定的路径下

2.使用静态库编译文件

gcc -o app app.c -Iinclude -L./ -lother1 -lother2

-l: 指示编译器,装载的函数库。该函数库位于系统默认的路径(包含/usr/lib、/usr/lib64以及./),或通过-L选项指定库所在的路径

3.相关命令(ar命令)

  • Linux ar命令是一个用于创建、修改或查询备存文件(archive)的命令行工具,它可以将多个文件(通常是目标文件.o)打包成一个单一的文件(通常是静态库文件.a),并保留文件的属性和权限
  • Linux ar命令的基本语法是:

ar [选项]... [备存文件] [成员文件]...

其中,[备存文件]是指定要创建或操作的备存文件的名称,[成员文件]是指定要添加或删除的文件的名称,可以是一个或多个。[选项]是用来控制ar命令的行为和功能的参数,例如:

  • -d:删除备存文件中的指定成员文件。
  • -r:替换或插入指定成员文件到备存文件中。
  • -t:列出备存文件中的所有或指定成员文件。
  • -x:从备存文件中提取指定成员文件。
  • -a <成员文件>:将新成员文件添加到指定成员文件之后。
  • -b <成员文件>:将新成员文件添加到指定成员文件之前。
  • -c:创建新的备存文件,不显示任何信息。
  • -v:显示详细的执行过程信息。

举例

下面是ar命令的一些常用选项和用法:

1. 创建静态库文件:
   ar cr libname.a file1.o file2.o ...
   例如:ar cr libmath.a add.o subtract.o multiply.o divide.o

2. 向静态库文件中添加目标文件:
   ar r libname.a file.o
   例如:ar r libmath.a power.o

3. 从静态库文件中删除目标文件:
   ar d libname.a file.o
   例如:ar d libmath.a divide.o

4. 列出静态库文件中包含的目标文件:
   ar t libname.a
   例如:ar t libmath.a

5. 提取静态库文件中的目标文件:
   ar x libname.a
   例如:ar x libmath.a

6. 显示静态库文件的详细信息:
   ar tv libname.a
   例如:ar tv libmath.a

 4.项目结构

/usr/local

在Linux系统中,/usr/local目录通常用于存放本地安装的软件和程序,而不是通过软件包管理器系统包管理器(如dpkg或yum)安装的软件。这个目录通常不被系统自带的程序使用,而是由系统管理员或用户自己安装或编译的程序使用。这些程序通常被安装在/usr/local/bin,/usr/local/sbin,/usr/local/lib等子目录中。 

/usr/local目录一般包含以下子目录:

- bin: 存放二进制文件。
- etc: 存放配置文件。
- lib: 存放库文件。
- sbin: 存放管理员用户使用的系统二进制文件。
- share: 存放共享数据(如帮助文档、图标等)。

总的来说,/usr/local目录提供了一个本地安装和自定义配置的机制,让用户在系统中安装和使用各种软件和程序,而不必影响系统本身的稳定性和安全性。

/usr/include 

/usr/include是一个标准的Linux系统目录,用于存储C编程语言所需的头文件。

头文件是一种包含函数原型、宏定义和结构体声明等信息的文本文件,它通常被包含在C源代码文件中,以便在编译时将其内容与源代码合并在一起。

在/usr/include目录中,你可以找到许多常用的C语言头文件,如stdio.h、stdlib.h、string.h等等。这些头文件包含了许多实用的函数和数据类型,可以帮助你完成各种编程任务。

在Linux系统中,如果你想编写C程序,那么/usr/include目录中的头文件是必不可少的资源。

/usr/lib64

/usr/lib64目录包含64位的共享库文件,是Linux系统中重要的目录之一。在64位Linux系统中,它存储了大多数程序和工具所需的共享库文件。这些共享库文件包括C标准库,数学库,网络库,OpenGL库等等。在这个目录下,还包括一些配置文件和二进制文件,供系统和应用程序使用。该目录的结构和内容会随着Linux系统版本和发行版的不同而有所变化。

 /usr/lib

目录 /usr/lib 是 Linux 系统中存放共享库和相关文件的标准位置之一,通常在该目录下存放着许多系统和应用程序所需的动态链接库(共享库)和静态链接库,如 C 库、X11 库、OpenGL 库等。此外,该目录下还存放着许多系统配置文件、插件和运行时文件等。在 Linux 系统中,通常将应用程序的可执行文件和动态链接库分离开来,便于管理和维护。

/usr/src 

"/usr/src" 目录是 Linux 内核源代码的默认安装位置。通常,它包含内核源代码及其编译后的文件。该目录也可能包含其他内核模块的源代码以及其他软件包的源代码。 

在该目录中,通常有一个名为 "linux" 的子目录,其中包含当前安装的内核的源代码。在编译和安装内核模块时,需要对该目录具有写入权限。

此外,在 "/usr/src" 目录中,可能还会包含其他子目录,如 "/usr/src/redhat"(在基于 Red Hat 的系统中)或 "/usr/src/debian"(在 Debian 系统中)。这些子目录可能包含特定 Linux 发行版的定制化内核源代码。

 自定义项目

 2.5动态库的创建和使用

动态库的创建步骤

(1)在头文件(.h)中声明动态库所导出的函数

(2)在源文件(.c)中实现动态库所导出的函数

(3)编译源文件,生成与位置无关的目标文件(.o)

(4)创建动态库

动态库的使用

(1)方式1

gcc [源文件] -L [动态库路径] -l [动态库名] -I [头文件路径] -o [可执行文件]

gcc app.c -Llib -ltest0x00 -I include -o app

./app  

ldd命令

ldd命令是Linux下的一个工具,用于查看可执行文件或共享库文件所依赖的动态链接库的列表。

语法:ldd [选项] [文件名]

选项:

- -v:显示详细信息;
- -u:显示未使用的库文件;
- -r:显示重定向信息;
- -d:给出库的全路径;
- -s:禁止显示重定向信息;
- -c:只显示库文件名,不显示其他信息。

示例:

ldd /bin/bash  # 查看bash可执行文件依赖的动态链接库列表
ldd /usr/lib/libstdc++.so.6  # 查看libstdc++.so.6共享库依赖的动态链接库列表

(2)方式2

gcc [源文件] -I [头文件路径] [库文件名(libxxx.so)] -o [可执行文件]

gcc app.c -I include ./lib/libtest0x00.so -o app

./app  

解决找不到链接库的方法

(1)更新LD_LIBRARY_PATH

export LD_LIBRARY_PATH=[自定义动态库路径]

只起到临时作用

LD_LIBRARY_PATH: 指定查找动态库的路径(除了默认路径),该路径在默认路径前进行查找。

(2)配置环境变量

(3)直接将动态库拷贝至/usr/lib等系统目录下(可行但强烈不推荐)

(4)更新/etc/ld.so.conf文件

将动态库的绝对路径写入/etc/ld.so.conf文件,后使用ldconfig命令更新

ldconfig命令

ldconfig是Linux系统下的一个命令,用于配置共享库的动态链接器运行时绑定。它的主要功能是更新共享库缓存,以便在运行时正确的找到共享库文件。

在Linux系统下,共享库文件默认存放在/lib和/usr/lib这两个目录下。当系统启动时,动态链接器会读取这些目录,并把其下的共享库文件加入到缓存中。当需要链接某个共享库时,动态链接器会从这个缓存中查找对应的共享库文件。

使用ldconfig命令可以手动更新共享库缓存,以便动态链接器能够找到最新的共享库文件。常用的ldconfig命令选项包括:

  • -v:显示执行过程中的详细信息。
  • -n:不执行实际的共享库缓存更新操作,而只输出要更新的共享库文件列表。
  • -p:显示当前系统的共享库缓存信息。
  • -r:使用指定的共享库缓存文件进行操作。

2.6相关文件说明

profile文件

Linux中的profile文件是一个脚本文件,它包含了在登录shell时执行的命令和变量设置。它通常位于用户主目录下的隐藏文件夹中,文件名为.profile。

该文件中的命令和变量设置会作为环境变量传递给子进程。它们可以用于设置用户的默认环境变量、路径、别名等。

例如,可以在.profile文件中添加以下行来设置PATH环境变量:

export PATH=$PATH:/usr/local/bin

这将将/usr/local/bin目录添加到PATH环境变量中。这意味着,每次登录shell时,用户都会自动添加该目录到他们的PATH环境变量中。

.profile文件中也可以设置shell别名,如:

alias ll='ls -alF'

这将为用户定义一个新的命令“ll”,它将执行“ls -alF”命令。

总之,Linux中的.profile文件可以用于自定义用户的默认环境变量、路径和别名等。

 bashrc文件

bashrc是Bash shell的一个配置文件,它包含了一些变量设置、别名定义、环境变量等内容。在Linux系统中,它通常位于用户的home目录下,并且在每个新的终端窗口打开时自动执行。

以下是一个bashrc文件的示例:

# 设置PATH变量,用于添加自定义二进制文件的搜索路径
export PATH=$PATH:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

# 设置别名
alias ll='ls -alh'
alias vi='vim'

# 设置PS1环境变量,用于自定义命令行提示符
PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '

上述示例中,首先设置了PATH变量,将一些常用路径添加到了搜索路径中。接着定义了两个别名,分别将ls命令显示为长格式并包含隐藏文件,并将vim命令替换为vi。最后,使用PS1环境变量自定义了一个绿色的命令行提示符,其中包含了当前用户名、主机名和当前工作目录名等信息。

当用户打开新的终端窗口时,这些设置会自动生效,使得命令行操作更加高效和方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烟雨平生9527

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值