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字,内容详实,包含理论、实践和总结,符合技术博客的写作规范。你可以直接使用或根据需要进行微调。

相关推荐
RisunJan4 小时前
Linux命令-rlogin(远程登录)
linux·运维
深圳恒讯4 小时前
菲律宾云服务器与传统VPS的架构差异
运维·服务器·架构
蓝天下的守望者4 小时前
svt_apb_if里的宏定义问题
运维·服务器·数据库
小林ixn4 小时前
从“玩具工具”到“跨语言利器”:MCP 协议实战解析
运维·服务器·网络
小顿的企业观察5 小时前
中企出海战略规划,正在从“走出去”转向“走进去”
大数据·运维·人工智能·产品运营·制造
数智化转型推荐官5 小时前
2026统一身份管理系统五大发展趋势解读
java·运维·微服务
看菜鸡互6 小时前
探索用 SlideML 让大模型生成 PPT 的实验方法
java·运维
大飞记Python7 小时前
Linux命令速查手册(测试开发4年实战总结,附PDF)
linux·网络·pdf
莫成7 小时前
git相关命令查阅
git
liguojun20258 小时前
篮球馆自动计时收费系统:从规则配置到自动结算的全流程拆解
java·大数据·运维·人工智能·物联网·1024程序员节