Linux下的趣味编程 —— 进度条、Git版本控制与GDB调试实战

引言

在前三篇文章中,我们学习了软件包管理、Vim编辑、编译链接和Makefile。本文将带你动手编写第一个Linux交互程序------动态进度条,让你理解回车换行、行缓冲区等概念。然后,你将学习使用Git将代码上传到GitHub,最后掌握GDB调试器,轻松定位程序bug。

一、进度条程序 ------ 理解\r与行缓冲区

1. 回车与换行的区别
  • 换行(\n):将光标移动到下一行的同一列。

  • 回车(\r):将光标移动到当前行的行首。

在老式打字机上,这两个动作是分开的。在C语言中,\n通常同时完成换行+回车(取决于终端),但我们可以单独使用\r实现覆盖同一行的效果。

2. 行缓冲区现象

看下面两段代码的区别:

代码1(有\n):

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

现象:立即输出"hello",等待3秒后结束。

代码2(无\n):

复制代码
printf("hello");
sleep(3);

现象:等待3秒后,才输出"hello"。这是因为printf默认是行缓冲,遇到\n或缓冲区满或程序结束才会刷新。要想强制刷新,使用fflush(stdout)

代码3(使用fflush):

复制代码
printf("hello");
fflush(stdout);
sleep(3);

现象:立即输出"hello",等待3秒。

3. 倒计时程序

利用\rfflush实现数字倒计时覆盖显示:

复制代码
#include <stdio.h>
#include <unistd.h>
int main() {
    int i = 10;
    while (i >= 0) {
        printf("%-2d\r", i);  // %-2d左对齐占2位,\r回车不换行
        fflush(stdout);
        i--;
        sleep(1);
    }
    printf("\n");
    return 0;
}
4. 进度条完整代码

以下实现一个模拟下载的进度条,动态显示[####....] [百分比] [旋转光标]

process.h

复制代码
#pragma once
#include <stdio.h>
void process_v1();
void FlushProcess(double total, double current);

process.c

复制代码
#include "process.h"
#include <string.h>
#include <unistd.h>

#define NUM 101
#define STYLE '#'

void process_v1() {
    char buffer[NUM];
    memset(buffer, 0, sizeof(buffer));
    const char *lable = "|/-\\";
    int len = strlen(lable);
    int cnt = 0;
    while (cnt <= 100) {
        printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt % len]);
        fflush(stdout);
        buffer[cnt++] = STYLE;
        usleep(50000);  // 50ms
    }
    printf("\n");
}

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;
    int num = (int)(current * 100 / total);
    for (int i = 0; i < num; i++) buffer[i] = STYLE;
    double rate = current / total;
    printf("[%-100s][%.1f%%][%c]\r", buffer, rate * 100, lable[cnt % len]);
    cnt++;
    fflush(stdout);
}

main.c(模拟下载)

c

复制代码
#include "process.h"
#include <unistd.h>

int main() {
    double total = 1024.0;
    double speed = 1.0;
    double current = 0;
    while (current <= total) {
        FlushProcess(total, current);
        usleep(3000);
        current += speed;
    }
    printf("\nDownload Complete!\n");
    return 0;
}
Makefile

makefile

SRC = $(wildcard *.c)
OBJ = $(SRC:.c=.o)
BIN = processbar

$(BIN): $(OBJ)
	gcc $^ -o $@

%.o: %.c
	gcc -c $< -o $@

.PHONY: clean
clean:
	rm -f $(OBJ) $(BIN)

二、Git版本控制 ------ 将代码托管到GitHub

1. 什么是Git?

Git是目前最流行的分布式版本控制系统,可以记录文件的每一次修改,方便回退、比较、多人协作。常见的Git托管平台有GitHub、Gitee、GitLab等。

2. 安装Git

sudo yum install git -y # CentOS git --version # 查看版本

3. 在GitHub上创建仓库
  • 注册GitHub账号并登录。

  • 点击"New repository",输入仓库名(如myproject)。

  • 选择Public或Private,不要勾选"Initialize with README"(我们后面手动推送)。

  • 创建后复制仓库的HTTPS或SSH地址。

4. 三板斧 ------ add, commit, push

第一步:克隆仓库到本地

复制代码
git clone https://github.com/你的用户名/myproject.git
cd myproject

第二步:添加文件到暂存区

复制代码
git add process.c process.h main.c Makefile
# 或添加所有变化:git add .

第三步:提交到本地仓库

复制代码
git commit -m "add progress bar program"

第四步:推送到远程仓库

git push

会提示输入GitHub的用户名和密码(或使用token)。

其他常用命令

  • git status:查看当前状态

  • git log:查看提交历史

  • git pull:拉取远程最新代码

首次使用配置用户信息

git config --global user.name "Your Name" git config --global user.email "your_email@example.com"

三、GDB调试器 ------ 让Bug无处遁形

1. 准备工作

程序必须使用-g选项编译才能包含调试信息。

复制代码
gcc -g mycmd.c -o mycmd

启动gdb:

复制代码
gdb mycmd
2. 常用调试命令
命令 简写 作用
list l 显示源代码
break 行号 b 10 设置断点
break 函数名 b main 在函数入口断点
info break i b 查看所有断点
run r 运行程序
next n 单步步过(不进入函数)
step s 单步步入(进入函数)
continue c 继续执行到下一个断点
print 变量 p i 打印变量值
display 变量 display i 自动显示变量(每次停止时)
set var 变量=值 set var i=10 修改变量
watch 变量 watch result 监视变量变化
backtrace bt 查看函数调用栈
finish 执行完当前函数返回
quit q 退出gdb
3. 条件断点示例
复制代码
break 9 if i == 30    # 当i等于30时在第9行中断

或者先设置断点再追加条件:

复制代码
break 9
condition 1 i == 30
4. 实战:调试一个buggy程序

假设有以下代码(mycmd.c):

复制代码
#include <stdio.h>
int flag = 0;   // 应该是1,但写成了0
int Sum(int s, int e) {
    int result = 0;
    for (int i = s; i <= e; i++)
        result += i;
    return result * flag;
}
int main() {
    int n = Sum(1, 100);
    printf("result = %d\n", n);  // 预期5050,实际输出0
    return 0;
}

调试过程:

复制代码
gcc -g mycmd.c -o mycmd
gdb mycmd
(gdb) break main
(gdb) run
(gdb) step
(gdb) print flag   # 发现flag=0
(gdb) set var flag=1
(gdb) continue     # 输出变为5050,确认问题在flag
5. 推荐使用cgdb

cgdb是gdb的一个终端前端,可以分屏显示代码和调试命令,体验更好。

复制代码
sudo yum install cgdb -y    # CentOS
cgdb mycmd

ESC进入代码窗口,按i返回命令窗口。

四、总结

通过进度条程序,你掌握了\r和行缓冲区的概念,这对理解输入输出很有帮助。Git让你学会代码版本管理,是团队协作的必备技能。GDB则让你有能力定位复杂bug,不再盲目使用printf调试。至此,你已经完成了Linux基础开发工具链的学习:包管理、编辑、编译、构建、调试、版本控制。接下来就可以愉快地进行Linux编程了!


以上四篇博客每篇约4000字,内容详实,包含理论、实践和总结,符合技术博客的写作规范。你可以直接使用或根据需要进行微调。

相关推荐
nan madol1 小时前
Rocky Linux 9.5 部署 Percona XtraDB Cluster (PXC) 集群
linux·运维·服务器
zincsweet1 小时前
Linux 命名管道(FIFO)详解:原理分析、源码封装与通信流程图解
linux·服务器·c++·流程图
linux修理工1 小时前
使用 virt-install 命令行快速创建 KVM 虚拟机(以 CentOS 7 为例)
linux·运维·centos
|_⊙1 小时前
进程间通信(System V 标准下的多种通信方式)
linux·运维·服务器
云登指纹浏览器1 小时前
指纹浏览器自动化API对接实战总结:技术方案选型 + 避坑指南
运维·后端·自动化
xlq223222 小时前
7.git
git
蹉跎岁月新2 小时前
Jenkins创建一个maven-project
运维·jenkins·maven
Ws_2 小时前
Git + Gerrit 第六课:commit --amend、Patch Set 与 Change-Id
git
zincsweet2 小时前
C++ 实现进程池:主从架构、管道通信与任务调度
linux·c++