【Linux】基础开发工具(gcc/g++,makefile)

本文是小编巩固自身而作,如有错误,欢迎指出!

目录

[一、 程序的翻译四步骤](#一、 程序的翻译四步骤)

二、程序的具体编译过程​编辑

[(1)C 程序编译(gcc):](#(1)C 程序编译(gcc):)

[(2)C++ 程序编译(g++):](#(2)C++ 程序编译(g++):)

(3)分步编译(更灵活,适合多文件项目)

[​编辑 三、动态链接和静态链接](#编辑 三、动态链接和静态链接)

四、makefile的使用

(1)makefile的使用方法

(2)第一个系统项目------进度条

行缓冲区

[version 1](#version 1)

version2

version3


  • GCC (GNU Compiler Collection):GNU 编译器套件,是一套支持多种编程语言的编译器,默认编译 C 语言 程序。
  • G++ :是 GCC 的一个前端,专门用于编译 C++ 程序 ,本质上也是调用 GCC 核心,但会自动链接 C++ 标准库(如 libstdc++),而 GCC 编译 C++ 时需要手动指定链接

一、 程序的翻译四步骤

  1. 预处理(进⾏宏替换/去注释/条件编译/头⽂件展开等)

  2. 编译(⽣成汇编)

  3. 汇编(⽣成机器可识别代码)

  4. 连接(⽣成可执⾏⽂件或库⽂件)

二、程序的具体编译过程

(1)C 程序编译(gcc)

cpp 复制代码
// hello.c
#include <stdio.h>
int main() {
    printf("Hello, GCC!\n");
    return 0;
}
bash 复制代码
# 编译:gcc 源文件 -o 可执行文件名
gcc hello.c -o hello
# 运行
./hello

输出:Hello, GCC!

(2)C++ 程序编译(g++)

cpp 复制代码
// hello.cpp
#include <iostream>
using namespace std;
int main() {
    cout << "Hello, G++!" << endl;
    return 0;
}

(3)分步编译(更灵活,适合多文件项目)

bash 复制代码
# 1. 预处理(生成 .i 文件,展开头文件、宏)
g++ -E hello.cpp -o hello.i

# 2. 编译(生成 .s 汇编文件)
g++ -S hello.i -o hello.s

# 3. 汇编(生成 .o 目标文件)
g++ -c hello.s -o hello.o

# 4. 链接(生成可执行文件)
g++ hello.o -o hello_cpp

核心常用参数

三、动态链接和静态链接

在我们的实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数,但是每个源⽂件都是独⽴编译的,即每个*.c⽂件会形成⼀个*.o⽂件。

为了满⾜前⾯说的依赖关系,则需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序。这个链接的过程就是静态链接。

静态链接的缺点很明显:

浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本;

更新⽐较困难:因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程序。但是静态链接的优点就是,在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在执⾏的时候运⾏速度快。

动态链接的出现解决了静态链接中提到问题。动态链接的基本思想是把程序按照模块拆分成各个相对独⽴部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,序模块都链接成⼀个单独的可执⾏⽂件。

我们的C程序中,并没有定义"printf"的函数实现,且在预编译中包含的"stdio.h"中也只有该函数的声明,⽽没有定义函数的实现,那么,是在哪⾥实"printf"函数的呢?

最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库⽂件中去了,在没有特别指定,gcc 会到系统默认的搜索路径"/usr/lib"下进⾏查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数"printf"了,⽽这也就是链接的作⽤。

四、makefile的使用

什么是构建

代码构建(Build)是指将人类可读的源代码(如 .c, .cpp, .java, .py 等)及其相关资源文件(如头文件 .h,配置文件,图像,库文件等),通过一系列自动化工具和步骤,转换、组合和打包成计算机可以直接执行或部署的目标形式的过程

构建的步骤

预处理、编译、汇编、链接、代码生成、资源处理、代码优化、打包、测试、文档生成


make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建。

(1)makefile的使用方法

简而言之make更像一个钥匙,而makefile则是储存东西的箱子,当我们使用make时,就会把箱子里的东西拿出来,具体演示可以看看下例:

bash 复制代码
#include <stdio.h>
int main()
{
 printf("hello Makefile!\n");
 return 0;
}

test1.exe由test.c产生,这就是他们其中依赖关系,使用:关联

(2)第一个系统项目------进度条

下面我们进行第一个运用makefile的项目的编写。

所谓的进度条,其实也就是将一些特殊符号打印在屏幕中,但是我们总会在其中遇到一些问题,我们接下来就来看看这些问题具体怎么解决

行缓冲区

我们分别将下列两行代码运行以下看看有什么区别

bash 复制代码
#include <stdio.h>
int main()
{
 printf("hello world!\n");
 sleep(3);
 return 0;
}
bash 复制代码
#include <stdio.h>
int main()
{
 printf("hello world!");
 sleep(3);
 return 0;
}

在运行后我们会发现一个问题,就是为什么第一个程序是先打印出字符串然后三秒后结束程序,而第二个程序是三秒后打印出字符串然后结束程序。

原因:标准输出stdout默认采用行缓冲模式。行缓冲规则为遇到换行符\n时自动刷新缓冲区。

简单了解一下我们创建项目需要解决的较重要的问题,现在我们看看项目的具体编写过程

version 1

cpp 复制代码
// version 1:控制台进度条实现
void Process()
{
    // 定义旋转动画的字符序列(| / - \),用于加载动态效果
    const char *lable="|/-\\";
    // 获取动画字符序列的长度(这里是4)
    int len=strlen(lable);

    // 定义进度条缓冲区,长度为SIZE(需提前定义,比如#define SIZE 101)
    char processbuff[SIZE];
    // 将缓冲区初始化为全空字符'\0',确保初始状态为空
    memset(processbuff, '\0', sizeof(processbuff));

    // 进度计数器,从0到100表示0%到100%
    int cnt = 0;
    // 循环:进度从0%到100%
    while(cnt <= 100)
    {
        // 打印进度条:
        // %-100s:左对齐占100字符宽度的进度条
        // %d%%:显示当前进度百分比
        // %c:显示当前动画字符(循环切换| / - \)
        // \r:回车符,让光标回到行首,实现覆盖更新
        printf("[%-100s][%d%%][%c]\r", processbuff, cnt, lable[cnt%len]);
        // 强制刷新输出缓冲区,确保进度条实时显示
        fflush(stdout);
        // 进度缓冲区的第cnt位置填充STYLE(比如'#'或'='),然后cnt自增
        processbuff[cnt++] = STYLE;
        // 暂停50毫秒(50000微秒),控制进度条动画速度
        usleep(50000);
    }
    // 进度完成后换行,避免后续输出和进度条重叠
    printf("\n");
}

version2

cpp 复制代码
void FlushProcess(double total, double current)
{
    char buffer[NUM];
    memset(buffer, 0, sizeof(buffer));
    const char *lable="|/-\\";
    int len = strlen(lable);
    static int cnt = 0;
    // 新增:标记进度是否已完成(静态变量,多次调用保持状态)
    static int is_finished = 0;

    // ========== 核心修复1:边界处理 ==========
    // 防止current超过total导致进度条越界,同时避免浮点精度问题
    if (current >= total) {
        current = total; // 强制将进度锁定为100%
        // 仅在第一次完成时执行结束逻辑,避免重复换行
        if (!is_finished) {
            // 填充100%的进度条
            int i = 0;
            for (; i < 100; i++) {
                buffer[i] = STYLE;
            }
            // 打印最终100%进度条,用\n替代\r,结束刷新并换行
            printf("[%-100s][100.0%%][%c]\n", buffer, lable[cnt % len]);
            fflush(stdout);
            is_finished = 1; // 标记为已完成,后续调用不再刷新
        }
        return; // 进度完成后直接返回,不再执行后续刷新逻辑
    }

    // ========== 核心修复2:重置完成标记(如果进度回退) ==========
    if (is_finished) {
        is_finished = 0;
    }

    // ========== 原有逻辑保留(仅优化) ==========
    // 计算进度百分比(0~100),避免负数(比如current为0时)
    int num = (int)(current * 100 / total);
    // 限制num不超过100(双重保险)
    num = num > 100 ? 100 : num;
    int i = 0;
    for(; i < num; i++)
    {
        buffer[i] = STYLE;
    }

    double rate = current / total;
    cnt %= len;
    // 正常进度仍用\r覆盖刷新
    printf("[%-100s][%.1f%%][%c]\r", buffer, rate * 100, lable[cnt]);
    cnt++;
    fflush(stdout);
}

version3

cpp 复制代码
//version3
//相较于version2,这也是一种特殊的代码
//实际业务的时候,这种方法可以复用,代码维护性较好
//函数指针
 
typedef void (*callback_t)(double, double);
 
//网络浮动
//              基本网速      浮动网速
double speedfloat(double start, double range)//示例(1.0,3.0)->[1.0,4.0]
{
    int int_range = (int)range;
    return start + rand() % int_range + (range + int_range);
}
//cb:回调函数
void download(int total, callback_t cd)
{
    srand(time(NULL));
    double cur = 0.0;
    while (1)//(cur<=total)
    {
        if (cur > total)
        {
            cur = total;//模拟下载完成
            cd(total, cur);//更新进度:按照下载进度更新进度
            break;
        }
 
        cd(total, cur);//更新进度:按照下载进度更新进度
        //cur+=speed;//模拟下载行为
        cur += speedfloat(speed, 30.3);
 
        Sleep(300);//模拟网络延迟
        //如果在Linux系统下,可以使用usleep代替Sleep,单位为微秒
    }
}

运行结果本次分享就到这里结束了,后续会继续更新,感谢阅读!

相关推荐
爱丽_1 小时前
Linux 目录结构与常用命令速查(服务器必备)
linux·运维·服务器
卢傢蕊1 小时前
Nginx 核心功能
运维·服务器·网络
卤炖阑尾炎2 小时前
Nginx 核心功能全解析:正向代理、反向代理、缓存与 Rewrite 实战
运维·nginx·缓存
ShineWinsu2 小时前
2026年AI Agent变现新思路:告别传统电商,拥抱自动化矩阵与服务套利
运维·人工智能·自动化
yatum_20142 小时前
集群节点时钟同步(NTP)配置手册
linux·分布式·hbase
yangchanghua1112 小时前
Nacos如何开启ssl(https)[图文版]
服务器·https·ssl
椰奶燕麦2 小时前
Linux 服务开机自启配置指南 (基于 Crontab)
linux
路溪非溪2 小时前
systemd简介和使用总结
linux·arm开发·驱动开发
星竹晨L2 小时前
Linux入门:权限管理的核心逻辑
linux