引言
在前三篇文章中,我们学习了软件包管理、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. 倒计时程序
利用\r和fflush实现数字倒计时覆盖显示:
#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字,内容详实,包含理论、实践和总结,符合技术博客的写作规范。你可以直接使用或根据需要进行微调。