【Linux】gcc/g++ 、make/Makefile、git、gdb 的使用

目录

  • [1. Linux编译器-gcc/g++](#1. Linux编译器-gcc/g++)
    • [1.1 编译器gcc/g++的工作步骤](#1.1 编译器gcc/g++的工作步骤)
    • [1.2 函数库](#1.2 函数库)
    • [1.2.1 函数库的作用及分类](#1.2.1 函数库的作用及分类)
      • [1.2.2 动态链接和静态链接](#1.2.2 动态链接和静态链接)
      • [1.2.3 动态库和静态库的优缺点](#1.2.3 动态库和静态库的优缺点)
    • [1.3 gcc选项](#1.3 gcc选项)
  • [2. Linux项目自动化构建工具-make/Makefile](#2. Linux项目自动化构建工具-make/Makefile)
    • [2.1 .PHONY](#2.1 .PHONY)
    • [2.2 尝试编写进度条程序](#2.2 尝试编写进度条程序)
  • [3. git](#3. git)
    • [3.1 安装 git](#3.1 安装 git)
    • [3.2 下载项目到本地](#3.2 下载项目到本地)
    • [3.3 三板斧第一招: git add](#3.3 三板斧第一招: git add)
    • [3.3 三板斧第二招: git commit](#3.3 三板斧第二招: git commit)
    • [3.4 三板斧第三招: git push](#3.4 三板斧第三招: git push)
    • [3.5查看三板斧的情况:git status](#3.5查看三板斧的情况:git status)
  • [4. Linux调试器-gdb](#4. Linux调试器-gdb)
    • [4.1 gdb的使用](#4.1 gdb的使用)

1. Linux编译器-gcc/g++

1.1 编译器gcc/g++的工作步骤

  1. 预处理(进行宏替换)
    实例: gcc --E hello.c --o hello.i
  2. 编译(生成汇编)
    实例: gcc --S hello.i --o hello.s
  3. 汇编(生成机器可识别代码)
    实例: gcc --c hello.s --o hello.o
  4. 链接(生成可执行文件或库文件)
    实例: gcc hello.o --o hello

1.2 函数库

1.2.1 函数库的作用及分类

函数库是在链接过程中涉及的重点

  • 我们的C程序中,并没有定义"printf"的函数实现,且在预编译中包含的"stdio.h"中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实"printf"函数的呢?
  • 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径"/usr/lib"下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数"printf"了,而这也就是链接的作用

函数库一般分为静态库和动态库两种。

  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为".a"
  • 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。

1.2.2 动态链接和静态链接

动态链接和静态链接:

  • 在Linux中,编译形成可执行程序,默认采用的就是动态链接--提供动态库

    tatic

  • 在Linux中,如果要按照静态链接的方式,进行形成可执行程序,需要添加-static选项 -- 提供静态库

  • 如果我们没有静态库,但是我们就要-static,行不行呢? 不行

  • 如果我们没有动态库,只有静态库,而且没有添加-static选项,gcc能找到吗-----能的,gcc默认优先动态链接,若没有动态库或动态库里没找到,就会使用静态链接,去静态库

  • static的本质:改变优先级,并且让所有的连接要求全部变成静态链接

  • 链接不一定是纯的全部动态链接或者静态链接,有时是混合的!

1.2.3 动态库和静态库的优缺点

动态库

  • 优:动态库因为是共享库,有效的节省资源(磁盘空间,内存空间,网络空间等)
  • 缺:动态库一旦缺失,导致各个程序都无法运行

静态库

  • 优:静态库,不依赖库,程序可以独立运行
  • 缺:体积大,比较消耗资源

1.3 gcc选项

  • -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件(.i后缀)里面
  • -S 编译到汇编语言不进行汇编和链接(.s后缀)
  • -c 将汇编语言翻译成机器可识别代码(即可重定位目标二进制文件,简称目标文件) (.o后缀,.obj文件)
    目标文件:该类文件虽然已经是二进制文件,但是仍不能执行,需要经过链接才能执行
  • -o 文件输出到指定文件
  • -static 此选项对生成的文件采用静态链接
  • -g 生成调试信息。GNU 调试器可利用该信息。
  • -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
  • -O0
  • -O1
  • -O2
  • -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
  • -w 不生成任何警告信息。
  • -Wall 生成所有警告信息。

2. Linux项目自动化构建工具-make/Makefile

  • makefile带来的好处就是------"自动化编译",一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
  • make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建
  • make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件


2.1 .PHONY

.PHONY:使指令总是被执行

我们有时会看到这样的情况:

这是因为 mycode.c 文件已经生成了 mycode 文件,且之后没有修改 mycode.c 文件。

这样设计的优点:提高了效率

原理:通过对比 mycode.c 文件 和 mycode 文件 的最近修改时间的先后 来确定mycode.c 是否更新情况。

原理详细讲解:一定是源文件形成可执行,现有源文件,才有可执行,一般而言,源文件的最近修改时间比可执行文件要老。但如果我们更改了源文件,历史上曾经还有可执行,那么源文件的最近修改时间,一定要比可执行程序要新!所以只需要比较,可执行程序的最近修改时间和源文件的最近修改时间、(.exe 新于 .c 源文件是老的,不需要重新编译;.exe 老于.c源文件是老的,需要重新编译)

.PHONY:通过更改mycode.c 文件的修改时间(无论文件是否修改),来使mycode.c文件一定会被执行

2.2 尝试编写进度条程序

进度条程序的简单版(原理版):

c 复制代码
//processBar.c
#include"processBar.h"
#include<string.h>
#include<unistd.h>

char bar[NUM];
memset(bar,'\0',sizeof(bar));

   const char* icon="|/-\\";
void processBar()
{
   int len=strlen(icon);

   int cnt=0;
   while(cnt<=TOP)  //每循环一次进度+1,循环了101次,最后一次加的进度,进度条没有被记录到缓存区
   {
      printf("[%-100s][%d%%][%c] \r",bar,cnt,icon[cnt%len]);
      fflush(stdout);

      bar[cnt++]=STYLE;   //进度+1;
      if(cnt<=99)   bar[cnt]=STYLE_TAIL;

      usleep(TIME);  //0.05s,单位是微秒。 unistd.h是头文件
   }
   printf("\n");

}
c 复制代码
//processBar.h
#pragma once
  
#include<stdio.h>

#define NUM 102
#define STYLE  '-'
#define STYLE_TAIL '>'
#define TIME 50000    //0.05s   总:5s
#define TOP 100

extern void processBar();
c 复制代码
 //main.c:
#include"processBar.h"
#include<unistd.h>

int main()
{
    processBar();

    return 0;
}

输出结果截图:

修饰重装版(更完善、全面):

c 复制代码
//processBar.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"processBar.h"
#include<stdio.h>
#include<string.h>
#include<unistd.h>

//进度条
char bar[NUM];    //全局数组默认初始化为0

//进度进行的旋转标志
const char* icon = "|/-\\";


//把当前比率转化为进度条,并为下一次加一个小格(一个百分点)
//rate[0,100]
void processBar(int rate)
{
    if (rate < 0 || rate>100)   return;
    int len = strlen(icon);

    printf(ANSI_COLOR_GREEN"[%-100s]"ANSI_COLOR_END"[%d%%][%c]\r", bar, rate, icon[rate % len]);
    fflush(stdout);

    bar[rate++] = STYLE;
    if (rate <= 99)   bar[rate] = STYLE_TAIL; 
}

void initbar()
{
    memset(bar, '\0', sizeof(bar));
}
c 复制代码
//main.c
#define _CRT_SECURE_NO_WARNINGS 1

#include"processBar.h"
#include<unistd.h>

typedef void(*callback_t)(int);  //函数指针类型

void download(callback_t cb)
{
    int total = 1000;  //总共1000MB
    int curr = 0;      //现在共加载0MB

    while (curr <= total)
    {
        Sleep(50000);  //0.05s,模拟加载时间
       
        int rate = curr * 100 / total; //更新进度
        //processBar(rate);
        cb(rate);        //通过回调,显示进度
        curr += 10;  //因为processBar函数为下一个循环加载了一个百分点,
                     //所以curr要加上total*1%==10,进度条、百分号才会一一对应;
    }
    printf("\n");
    initbar();
}

int main()
{
    download(processBar);
    //download(processBar);
    return 0;
}
c 复制代码
//processBar.h
#pragma once

#include<stdio.h>

#define NUM 102
#define STYLE  '='
#define STYLE_TAIL '>'
#define TIME 50000    //0.05s   总:5s
#define TOP 100

#define ANSI_COLOR_GREEN   "\x1b[32m"
#define ANSI_COLOR_END   "\x1b[0m"


extern void processBar(int rate);

extern void initbar();

输出结果截图:

3. git

3.1 安装 git

c 复制代码
yum install git

3.2 下载项目到本地

创建好一个放置代码的目录。

这里的 url 就是刚刚建立好的 项目 的链接

c 复制代码
git clone [url]

3.3 三板斧第一招: git add

将代码放到刚才下载好的目录中

文件名是要上传的文件的名字

c 复制代码
git add [文件名]

3.3 三板斧第二招: git commit

提交改动到本地

c 复制代码
git commit -m "提交日志"

3.4 三板斧第三招: git push

c 复制代码
git push

3.5查看三板斧的情况:git status

c 复制代码
git status

4. Linux调试器-gdb

  • 使用程序的发布方式有两种,debug模式和release模式
  • Linux gcc/g++出来的二进制程序,默认是release模式
  • 要使用gdb调试,必须在源代码生成二进制程序(gcc编译时)的时候, 加上 -g 选项,即必须是debug模式

4.1 gdb的使用

  • list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
  • list/l 函数名:列出某个函数的源代码。
  • r或run:运行程序
  • break(b) 行号:在某一行设置断点
  • break 函数名:在某个函数开头设置断点
  • info break :查看断点信息。
  • delete breakpoints:删除所有断点
  • delete breakpoints n:删除序号为n的断点
  • n 或 next:单条执行,逐过程,不进入函数
  • s或step:进入函数调用,逐语句,进入函数
  • p 变量:打印变量值。
  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值
  • undisplay 编号:取消对先前设置的那些变量的跟踪
  • until X行号:跳至X行
  • finish:执行到当前函数返回,然后挺下来等待命令,可以快速的判断问题是不是出在某一个函数里
  • c:从一个断点运行到另一个断点,与 r 功能差不多
  • disable 断点编号:禁掉断点
  • enable 断点编号:解除断点被禁状态
  • quit、ctrl + d:退出gdb
相关推荐
broad-sky11 小时前
Ubuntu上查看USB相机连接的是哪个口,如何查看
linux·数码相机·ubuntu
秋深枫叶红11 小时前
嵌入式第三十七篇——linux系统编程——线程控制
linux·学习·线程·系统编程
shaohui97312 小时前
ARMv7 linux中断路由以及处理
linux·gic·cpsr·armv7
三小尛12 小时前
linux的开发工具vim
linux·运维·vim
陈陈爱java12 小时前
Conda 常用命令行
linux·windows·conda
twdnote12 小时前
dokcer 环境中集成LibreOffice
linux
凯子坚持 c12 小时前
Git 远程仓库操作与深度进阶指南
git
勇敢牛牛_12 小时前
RustRover 2025.3 在WSL中GIT操作十分缓慢的问题
git·rust·rustrover
ChristXlx12 小时前
Linux安装redis(虚拟机适用)
linux·运维·redis
源文雨12 小时前
PVE实现USB硬盘盒在备份前自动上电/结束后自动断电脚本
linux·运维·服务器·备份·perl·pve·usb硬盘盒