
1. 为什么我建议你把Git和C语言放在一起入门1.1 C语言是写代码的起点Git是管代码的起点先讲一个我经常遇到的现象。总有刚入门的同学问我“C语言还没写明白现在学Git是不是太早了”我的回答永远是不早真的不早。C语言作为很多高校的第一门编程课它让你理解变量、内存、指针和程序运行的本质而Git是当前每一个程序员都无法绕开的源码管理工具它帮你保存每一个能运行的版本也让你敢于随便折腾代码。一个是“把代码写出来”的功夫一个是“把代码管起来”的功夫两者本来就是互补的。把这两个技能同时纳入你的学习计划你会在很早期就建立起“写代码 存代码”的完整闭环。很多初学者被厚厚的教材吓住以为要把C语言完全掌握才能学Git。其实完全不需要。C语言可以先抓主干变量、分支、循环、数组、函数、指针、结构体Git可以先抓动作安装、配置、提交、推送、分支。两个技能的入门曲线都比较长但把它们穿插着学你会发现进度反而更快。我今天写一个C语言小程序提交一次明天改进它再提交一次。每一次提交都同时巩固了两个知识点这种正反馈是单独学任何一项都得不到的。还要强调一点Git带来的“存档安全感”对新手尤其重要。我见过太多学生把代码改崩了又不好意思问只能从头再来。如果你在动手前就初始化一个Git仓库每次改完一个功能就提交就算后面改成一团乱麻也能从容地回退。心态稳了学习效率自然就上来了。学习编程的第一课不只是学会语法还要学会如何安全管理自己的作品。1.2 两个技能如何配合学习效率才最高配合方法其实很简单用C语言练习驱动Git操作用Git提交记录倒逼C语言水平。比如你连续写十个C语言练习题每个练习题都是一个独立的提交版本这样你的git log看起来就像一张学习地图。我经常让学员把提交信息写得具体一点不要只写“update”而是写“实现冒泡排序并修复数组越界问题”这样以后回看时能清晰看到自己每个阶段的熟练程度和容易犯错的地方。另外一旦代码量超过500行没有版本管理你迟早会疯掉。我记得自己是从一个三千行的课设项目开始认真用Git的当时分了三个分支主分支放稳定版本dev分支放开发中的功能实验分支用来看某个思路能不能跑通。这个方法放在C语言练习里一样通用——你想尝试一个复杂指针实现开个分支尝试不行就删掉不会影响主版本。这种工作流带来的收益远不是多花几分钟敲命令能衡量的。2. C语言初步认识把地基打牢固2.1 从Hello World开始理解编译和运行C语言第一课基本都是Hello World但很多同学只是照着敲了一遍没有理解背后的编译过程。请看代码#include stdio.h int main() { printf(Hello, World!\n); return 0; }这里#include stdio.h表示引入标准输入输出头文件printf就声明在这里main是程序入口操作系统从这里启动你的程序return 0是可执行文件成功退出后返回给系统的状态码。如果手边有Linux或者安装了MinGW的Windows可以用这一条命令把源码变成可执行程序gcc hello.c -o hello ./hellogcc是编译器它把人类能读懂的C源码翻译成CPU能执行的机器指令-o hello指定输出的文件名。这个步骤走通你的开发环境就基本没问题了。很多同学卡在起步阶段不是因为语法有多难而是不知道“源代码”和“可执行程序”是两回事。在VS Code里如果配置好插件你也可以直接点运行按钮但本质还是先编译再运行。我在教入门者时总会刻意让他们先在命令行里手动敲一次gcc命令亲眼看到hello.c变成hello这个可执行文件。这样以后遇到任何运行报错你能大概判断问题是出在编译阶段还是运行阶段排查起来会快很多。2.2 变量、数据类型与printf/scanf的坑C语言让很多新手头疼的第一件事就是变量类型和格式化占位符要一一对应。初学时你只需要记住四种基本类型int、char、float、double。但每次写printf或scanf时你必须知道自己在和哪种类型打交道否则编译也许能通过运行结果却可能完全不对。下面这张表把这四种类型在32位系统里的常见字节数、典型使用场景和格式化占位符列在一起方便你随时查阅。类型常见大小32位系统典型使用场景格式化占位符int4字节计数、索引、普通整数%dchar1字节字符或小整数%c、%dfloat4字节单精度小数%fdouble8字节科学计算、精确小数%lf这里面的坑在于printf和scanf不认识变量的声明类型它只认占位符。你写printf(%d, ch)即使ch是char类型它也会把里面的内容按int格式打印有时会打印出ASCII值而scanf(%d, num)要求num必须是int地址如果你写成scanf(%d, num)而少写了一个程序直接访问非法地址轻则返回值异常重则当场崩溃。还有一个和热词相关的经典场景“变量用%d输入一个字符后的值”。假如你用scanf(%d, num)但控制台输入的是字符ascanf会匹配失败num不会被赋值而字符a还留在输入缓冲区里。下一句scanf(%d, num2)继续读时依然读不走这个a导致后续逻辑全部错乱。解决方法是关注scanf返回值如果返回0说明没有成功匹配任何值先清空缓冲区再继续int c; while ((c getchar()) ! \n c ! EOF) {}这段代码会把本行剩余字符全部读走相当于把输入缓冲清理干净。新手写控制台程序时这个技巧能帮你绕开“莫名其妙跳过输入”的坑。我见过不少学生在scanf和getchar之间反复横跳最终问题都在这个缓冲区残留上所以建议把这段清理代码背下来关键时刻直接套用。2.3 指针理解地址和间接访问指针是C语言里让新手最容易产生挫败感的内容但它本质就是“地址”。很多人在初学阶段被指针搞得晕头转向其实是因为教材一上来就抛出一堆二级指针、指针数组、函数指针。而我的观点是先忘掉那些花哨用法记住指针只是一个保存内存地址的变量。有了这个基础认知后面的一切都是在这个概念上做扩展。理解这一点之后你会发现指针没有想象中那么可怕至少它和普通变量一样有类型、有值、有地址只是它的值通常指向另一个变量的位置。不妨用一个生活例子来类比。想象你去酒店入住前台给你一张门卡上面写着房间号。变量名就是房间门口贴的名字而指针就是那张门卡——你拿着它就能找到那间房然后进去修改里面的东西。在C语言里定义一个指针变量并用它指向一个已有的intint a 10; int *p a; // p保存的是a的内存地址 *p 20; // 通过p修改a的值 printf(%d\n, a); // 输出20是取地址符*在声明和表达式里的含义不同。声明int *p是说明p是一个指向整型的指针单独写*p是解引用表示“取出p指向的那个变量”。数组和指针的联系更是无处不在int arr[5]里的数组名arr可以直接当作指向第一个元素的指针arr 1会向后移动一个int的大小即4个字节这是指针加法按类型步长移动的体现。也就是说指针不只是“地址”它还知道地址背后的数据有多宽这是指针运算能自动跳过整个元素的关键。很多同学写指针代码遇到段错误常见原因有三类指针没有初始值就解引用返回了函数内部局部变量的地址数组越界导致指针飞到不相干的内存区域。我的建议很朴素每个指针声明时立即初始化拿不到有效地址就置为NULL解引用前检查是否为NULL。养成这个习惯后调试时间至少少一半。我还建议大家一定要学会用VS Code的调试器观察指针亲眼看到p的值是一个类似0x7ffd...的地址*p是它指向的那个值你心里那根弦一下就通了。2.4 函数、栈与递归C语言运行时发生了什么学C语言离不开函数函数调用背后隐藏着一个叫“栈”的结构。我说它隐藏是因为你在写源码时几乎看不到它的身影但它一直在背后支撑着每一次函数调用。每当你调用一个函数系统就要为这次调用准备一块独立的存储区域用来放函数参数、局部变量和调用结束后的返回地址。这块存储区域被组织成后进先出的结构所以起了个中文名叫“栈”。理解栈之后你才能深刻理解为什么局部变量在函数退出后就失效了也能解释很多奇怪的运行时错误。有同学问单片机里用C语言编程是不是没有栈其实不是。任何C语言程序在运行时都会用到栈只是不同硬件平台上的栈大小差异很大。比如某些8位单片机的栈空间只有几十到几百字节函数调用一深栈就溢出了。这也是为什么在嵌入式C编程中不建议写深层递归。我在深圳做IoT项目时就吃过这个亏一个看起来没问题的递归函数在PC上跑到十万层都没事烧到单片机里却让系统直接重启最后查出来是栈溢出。这个教训让我彻底记住了“栈有容量递归要节制”这句话。递归其实就是函数自己调用自己。每次调用都会新开一个栈帧所以递归的深度受栈大小限制。初学者经常用递归写阶乘逻辑上没问题但深度一大就有风险。相比起来改成循环往往更稳。理解函数、栈、递归这组概念会让之后的调试和性能优化顺理成章。至少当你看到“Segmentation fault”时你会本能地想到有没有可能栈被撑爆了而不是对着屏幕发呆。2.5 数组、字符串和常用处理函数C语言处理字符串的方式和Python、Java这些语言差别很大。字符串在C语言里并不是一个单独的类型而是以\0结尾的字符数组。这意味着你在声明char name[20] Alice;时编译器会自动在末尾补上一个ASCII码为0的字符用来告诉字符串处理函数“到这里就结束了”。正因为有这个隐藏的结束符strlen才能算出长度strcat才能知道从哪里开始拼接strcpy才知道要复制多少个字节。理解这一点是处理所有字符串函数的起点。读字符串强烈推荐fgets而不是gets。gets不做边界检查输入一长素材就能改写相邻内存是出了名的危险函数。fgets的安全写法是char buf[100]; fgets(buf, sizeof(buf), stdin);因为它最多读99个字符能在输入过长时自动截断。但它有另一个特点会把换行符也读进来。所以我通常紧跟一行buf[strcspn(buf, \n)] \0;strcspn返回buf中第一个\n出现的位置把它替换成\0就完成了“去掉末尾换行”的操作。另外一个常见需求是把字符串按空格拆分成多个子串最省事的是strtokchar *token strtok(str, ); while (token ! NULL) { printf(%s\n, token); token strtok(NULL, ); }strtok会把分隔符改成\0所以它会修改原字符串。如果还要保留原串记得先复制一份。热词里提到的“字符串逆序”也是经典练习用双指针的写法核心是左右两个指针不断向中间靠拢并交换字符最后在中间相遇时停止。这段代码我在前面给过建议你亲手敲一遍运行几组输入尤其是空字符串和只有一个字符的字符串看看程序会不会越界。2.6 结构体把多个变量打包当你要描述一个学生、一本书或一条订单时零散的变量会非常难管理。在C语言里结构体允许你把不同数据类型组合成一个新的复合类型它相当于你自定义的一个“记录模板”。定义一个学生结构体struct Student { char name[50]; int age; double score; };然后这样使用struct Student s1; strcpy(s1.name, Tom); s1.age 18; s1.score 89.5; printf(%s %d %.1f\n, s1.name, s1.age, s1.score);使用结构体的关键有二一是用结构体变量名.成员名访问内部字段二是可以创建结构体数组来管理多个同类型对象。比如struct Student stu[50]就能模拟一个班的学籍表。链表节点也往往通过结构体定义里面有一个指向同类型结构体的指针形式如下struct Node { int data; struct Node *next; };这个next指针正是链表的精髓所在——每个节点都保存着下一个节点的地址这样就能把内存里本来不连续的数据串起来。我看过不少新手写链表时卡在“struct Node *next”这一句上其实它就是定义了一个“指向和自己同类型结构体”的指针并没有想象中那么神奇。结构体是面向对象思想在C语言里的第一种投影也是从“只会写小程序”跨向“能写中型系统”的桥梁。练到这一层你已经掌握了C语言里最常用的一套数据组织方式。2.7 经典算法练习冒泡排序和九九乘法表算法练习是检验语法熟练度的试金石。很多刚学完分支和循环的同学看到排序算法就会懵其实它就是把你已经学过的知识组合起来。以冒泡排序为例思路特别朴素把相邻的两个数比较一下如果顺序不对就交换一趟下来最大的数会像气泡一样冒到数组末尾。代码用两层循环就能完成外层控制本轮要确定的位置内层把相邻元素中较大的往后移动。代码非常典型建议你亲自敲一遍void bubble_sort(int arr[], int n) { for (int i 0; i n - 1; i) { for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; } } } }关键点在于内层循环的上限n - 1 - i每一轮外层循环结束时最大的数已经被推到数组尾部所以下一轮无需再比较那些已归位的位置。冒泡排序的时间复杂度是O(n²)效率不高但概念直观非常适合入门。如果你能独立把它写出来说明你基本掌握了数组、循环和临时变量交换三项技能。九九乘法表则更偏向循环控制和格式化输出。代码很短for (int i 1; i 9; i) { for (int j 1; j i; j) { printf(%d*%d%-2d , j, i, i * j); } printf(\n); }%-2d让每个等式左对齐并且占用两个字符宽度输出结果才会像阶梯一样整齐。这种细节处理看起来不起眼却说明你对printf的格式化控制有真实掌握。把这两个练习亲手写一遍、运行一遍再提交到Git仓库里你会对“C语言入门”这件事产生真实信心。很多人学C语言只停留在“看得懂例题”的层面却很少动手写。其实判断标准很简单你能否在空白文件里从零写出冒泡排序如果还不行就再练一遍这远比背十遍语法有用。3. Git初步认识安装、配置与核心命令3.1 安装Git并完成初始配置在Windows上安装Git最简单的方式是从官网git-scm.com下载安装包双击后一路Next。安装过程中有一个“选择默认编辑器”的选项建议选VS Code这样以后Git需要写提交信息时会自动打开VS Code。其他选项保持默认即可。安装完成后在开始菜单或VS Code终端里执行git --version能显示版本号就说明成功。Linux用户通常使用包管理器sudo apt install git装完后必须做的第一件事是配置身份信息。Git会把这些信息记录在每个提交里相当于给代码打上你的签名。这个签名很重要尤其是以后你和其他人协作时别人需要知道哪次提交是谁做的。命令如下git config --global user.name 你的名字 git config --global user.email 你的邮箱注意这里的名字和邮箱最好和你用的代码托管平台保持一致但也并不强制。如果想查看当前配置执行git config --list就能看到全局和仓库级的所有配置项非常直观。如果你之前配置错了用同样的命令重新执行一遍即可覆盖。热词里反复提到“git配置gitee密钥”。这一步是为了免密推送代码。先在终端执行ls ~/.ssh看看是否已经有id_rsa和id_rsa.pub两个文件。如果没有用下面的命令生成密钥对ssh-keygen -t rsa -b 4096 -C 你的邮箱生成过程中一路回车然后执行cat ~/.ssh/id_rsa.pub把输出的公钥内容全部复制粘贴到Gitee个人设置中的“SSH公钥”栏目。配置完成后用ssh -T gitgitee.com测试如果提示欢迎信息说明SSH链路已经打通。之后你使用git clone和git push这些走SSH协议的命令时系统会直接用密钥认证不需要每次输入密码。这里还要提醒一句私钥id_rsa是绝对不能泄露的它相当于你账号的钥匙谁拿到它谁就能操作你的仓库。3.2 本地仓库初始化、暂存、提交和查看历史很多教程一上来就给你数十条Git命令新手很容易被吓退。其实日常开发中90%的时间只需要记住一条核心工作流git init建仓库git add把改动放入暂存区git commit把暂存区内容固化成一次提交git status随时查看状态git log查看提交历史。这条工作流跑顺了你就已经掌握了Git最常用的部分。在项目目录里执行git init它会创建隐藏的.git目录。你可以随意写一个hello.c然后查看状态git status此时红色列表显示untracked文件意思是这个文件还没有被Git跟踪。执行git add hello.c后它变成绿色表示已经放入暂存区。再执行git commit -m first commit: hello world这条命令就把暂存区内容打包成了一个不可变的记录。为什么要分“暂存区”和“工作区”两步因为现实中你往往同时改了多个文件但有些改动还没完成、不急着提交。暂存区让你可以选择性地提交而不是把所有半成品都混进一次提交。这个设计非常实用也是新手最容易理解错的地方。打个比方git add是把货装进购物车git commit是结账你完全可以在购物车里挑几件先买单剩下的等会儿再说。再看历史用git log --oneline每行显示一条提交记录格式是“提交哈希值提交信息”。如果你想撤销上一次提交可以用git reset --soft HEAD~1它会保留改动内容但撤掉提交记录如果连改动一起丢弃则用git reset --hard HEAD~1。不过--hard要极其谨慎因为它会丢弃工作区中尚未提交的修改。刚开始用Git时我建议多执行git status看到哪一步卡住了再补课。等你熟悉了这套流程你会觉得版本管理像呼吸一样自然。3.3 连接远程仓库克隆、关联、推送和拉取本地仓库只能在本机看想备份到Gitee、GitHub或者在家里和公司电脑间同步代码就需要远程仓库。远程仓库可以理解成一个公共的代码邮局你本地提交的每一个版本都可以推送过去别人也能从中拉取。基本操作分两种情形。第一种远程仓库已经存在你想拿到本地git clone gitgitee.com:username/repo.git这样会把整个仓库连同提交历史全部下载到当前目录。执行完你会看到一个新的文件夹里面就是克隆下来的项目。第二种本地项目已经有了想推送到远程。在网页端创建一个空仓库然后回到本地执行git remote add origin gitgitee.com:username/repo.git git push -u origin masterremote add是给远程仓库起一个名字约定俗成叫originpush -u是首次推送并建立本地分支与远程分支的关联之后你再推送时只需要git push。如果要拉取别人在你之后提交的内容用git pull它相当于git fetch加git merge先把远程更新下载下来再尝试合并到当前分支。如果远程有其他人修改过同一个文件可能在pull时遇到冲突这个我们下面讲分支合并时一起说。另外排查远程配置用git remote -v可以看到已经关联的远程地址。如果最初用HTTPS地址想换成SSH用git remote set-url origin改一下即可。这几条命令都属于高频操作建议每天至少练一遍直到手比眼睛快。3.4 分支与合并让多条开发线互不干扰分支是Git里最值得学习的特性之一。它可以让你在同一个仓库里同时维护多个不同状态的版本。比如你的项目已经能运行但又想写一个实验性的功能这时可以直接开一个分支在分支里随便折腾。主分支始终保持稳定实验分支失败了就丢掉成功了再合并回主分支。这一特性来自Git的指针设计创建分支时只创建一个新的指针指向当前提交所以开销极小这也是Git分支比SVN分支好用得多的原因。创建一个分支并切换过去git branch feature git switch feature更快捷的方式是git switch -c feature一步完成创建和切换。在feature分支上提交若干次后切回主分支git switch master git merge feature如果两个分支修改了同一文件的相同区域Git会产生冲突并提示CONFLICT。打开冲突文件你会看到类似 HEAD和 feature的标记两边代码分别来自不同分支。你需要手动选择保留哪一份甚至把两边内容都融合起来。选定之后再执行git add和git commit就完成了冲突解决。别慌冲突不是错误它只是提醒你这里需要人工判断。我第一次碰到冲突时很害怕后来发现只要把标记之间的内容梳理清楚提交后一切都正常。关于命名我一直建议分支名要能表达意图比如feature/login-page、fix/array-out-of-range。千万别用test、aaa这种名字因为几周后你根本看不出这个分支是干嘛的。养成规范命名的习惯是学习Git过程中比较早就会获得回报的一件事。团队协作时良好的分支命名能让你一眼看懂开发节奏也能减少很多不必要的沟通成本。3.5 修正提交git commit --amend怎么用提交之后发现有问题这是每个程序员都会遇到的场景。如果提交还没有推送到远程可以使用git commit --amend来修改上一次提交。很多人第一次听到amend会以为是把提交撤销重新来其实它更接近“原地编辑”用一个新提交替换掉旧提交结果就是历史记录里不再出现原本那个错误提交。它有两个典型用途一是把漏掉的文件补进去二是修正写错的提交信息。补漏文件的完整流程是git add forgotten.c git commit --amend -m 完整且正确的提交说明执行后上一个提交会被这个新提交替换历史记录里不会出现“漏文件”的那段痕迹。如果只是想改提交信息git commit --amend --only -m 新提交信息这里有两个重要提醒。第一--amend会改变原提交的哈希值所以对于已经推送到共享仓库的提交不要轻易修改否则会打乱其他人的本地历史。第二在你的个人练习仓库或者尚未推送的改动里--amend是很安全的但要记得修改后用git push --force把本地新提交强制覆盖到远程因为远程还留着旧提交。--force属于危险操作使用前一定要确认不会影响到别的协作者。顺带回应一下热词里那个很长的命令git -c diff.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks。这是VS Code等GUI工具在后台调用Git时自动附加的配置参数目的是让diff输出更友好、让中文文件名正常显示、避免图形界面锁冲突。你不需要手动敲这些命令看到它不代表项目出了问题。如果日后遇到Git输出里中文文件名变成\xxx倒是可以手动执行一下git config --global core.quotepath false这个我从亲身经历来说非常有用。3.6 Git Bash和GUI工具怎么选Windows用户会频繁看到一个叫“Git Bash”的程序它其实是Git安装包自带的一个模拟Linux命令行的环境。在里面ls、cd、mkdir这些命令和Linux保持一致路径分隔符也是斜杠非常适合Windows用户提前适应服务器环境。很多教程都会让你打开Git Bash执行命令本质上和Windows自带的PowerShell里敲Git命令效果类似但Git Bash对新手更友好一点因为它默认的提示符和颜色更容易看懂。如果你热衷于图形界面VS Code的源代码管理面板就能满足日常需求左侧图标打开面板可以看到所有改动点加号暂存输入提交信息点击提交再点推送按钮同步远程。除此之外还有Sourcetree、TortoiseGit等专门GUI工具。但我的建议还是刚入门时手动敲命令行至少把git status、git add、git commit、git push、git pull这条主路跑通。因为图形界面虽然直观却容易让你忽略Git的“工作区-暂存区-提交”模型。等你理解了模型再用图形界面就会觉得每个按钮都显得顺理成章。就像学车一样先开手动挡理解离合器的原理再换自动挡遇到复杂路况才不会发怵。4. 实操用VS Code把C语言和Git串起来4.1 VS Code配置C语言编译运行环境VS Code并不是IDE它只是一个编辑器所以配置C语言环境需要额外装“编译器”和“扩展”。Windows用户推荐安装MinGW-w64安装时记住bin目录路径把它添加到系统环境变量Path中。验证方式是在终端运行gcc --version能输出版本信息就算配置成功。然后在VS Code扩展商店搜索“C/C”安装由微软出品的那个扩展它会提供语法高亮、代码补全、跳转定义和调试支持。再装一个“Code Runner”扩展写代码时可以在编辑器里右键选择“Run Code”快速运行当前文件。这两个扩展的组合基本能覆盖你初学阶段的所有需求。如果你愿意多花一点时间还可以配置调试环境。在.vscode目录下创建launch.json和tasks.json指定用gcc编译当前文件配合GDB调试器可以设置断点、查看变量地址和指针值。这个配置对研究C语言的指针非常有帮助你能直观看到a、p、*p的实时变化。很多初学者觉得调试难反倒一直用printf打日志其实在VS Code里敲几个断点就够了。有个小技巧在监视面板里添加a和p每次单步执行时都能看到地址和值的变化指针这个概念会在半小时内变得无比清晰。4.2 在VS Code里初始化Git仓库并管理提交VS Code的左侧侧边栏里有“源代码管理”图标点击后如果发现“初始化仓库”按钮就直接点。它会自动执行git init并刷新出当前目录下所有未跟踪文件。你可以在输入框里写提交信息然后点击“提交”按钮再弹出菜单选择暂存所有改动。做完这一步仓库里就有了第一个提交。如果你更习惯命令行按Ctrl打开集成终端手动敲git init、git add、git commit一样可行。在VS Code里文件列表的颜色非常直观未跟踪的文件显示字母U已修改但未暂存显示M已暂存显示A。把鼠标悬停在文件上能看到它们的含义。点击每个文件还能直接看到与上一次版本的差异对比这个“暂存与比较”功能对新手掌握状态概念帮助很大。我常常让学生先在图形界面里观察颜色变化再回到命令行敲同样的命令两边对照很快就能搞懂工作区、暂存区、本地仓库和远程仓库四者的关系。这里特别提醒C语言项目会产生一堆编译产物例如hello.exe、*.o和a.out这些不应该纳入版本管理。你需要在仓库根目录创建.gitignore文件写入*.exe *.o *.out a.out这样git add .就不会把二进制文件加进来提交记录也会干净很多。我见过有人把编译好的可执行文件也提交上去仓库体积瞬间膨胀不说别人clone下来还以为是源码。所以写.gitignore这个习惯越早养成越好几乎每个正规C项目都离不开它。4.3 一个完整案例写字符串逆序程序并提交到Gitee挑一个前面讲过的小程序把从写代码到推送远程的完整链路走一遍。这个案例虽然简单但包含了编写源码、本地提交、关联远程、推送分支四个关键步骤。完成它就等于打通了个人练习的标准流水线。先在VS Code中新建一个文件夹c-practice并打开创建reverse.c#include stdio.h #include string.h void reverse(char *str) { int len strlen(str); for (int i 0; i len / 2; i) { char temp str[i]; str[i] str[len - 1 - i]; str[len - 1 - i] temp; } } int main() { char buf[100]; printf(请输入字符串); fgets(buf, sizeof(buf), stdin); buf[strcspn(buf, \n)] \0; reverse(buf); printf(逆序结果%s\n, buf); return 0; }先用Code Runner运行一下输入“hello”如果输出“olleh”说明程序正确。然后执行以下命令完成本地仓库创建和第一次提交git init git add reverse.c .gitignore git commit -m 实现字符串逆序功能接着去Gitee新建一个空仓库仓库名就叫c-practice不要勾选初始化README因为你在本地已经有仓库了。创建完成后页面会给出关联命令使用SSH地址即可git remote add origin gitgitee.com:你的用户名/c-practice.git git push -u origin master推送成功后刷新网页就能看到reverse.c和.gitignore。之后每写一个新的C练习比如冒泡排序你都可以新建一个分支或者直接在master上提交逐步积累自己的代码库。我在教学生的过程中发现很多人学完命令后很快就忘但一旦完成一次“写程序提交推送”的完整流程就再也不会忘了。这个流程值得你在初学阶段多重复几次直到变成肌肉记忆。5. 新手常见问题与避坑指南5.1 常见报错速查表我在教学和实际使用中把新手最常见的报错和奇怪现象整理成了表格遇到问题可以按图索骥。表格里的每一项都是真实发生过的案例不是从文档里抄来的标准定义。你可以把这张表保存下来遇到问题时先对号入座再看具体的解决方案。报错或现象可能原因解决方案fatal: not a git repository (or any of the parent directories): .git当前目录或上级目录并没有.git仓库先执行git init或者cd到仓库目录里再操作git: commit is not a git command命令拼写错误或Git安装不完整仔细检查拼写执行git --version确认安装正常gcc 不是内部或外部命令gcc不在系统PATH中确认MinGW的bin目录已加入PATH并重启终端scanf输入字符后程序跳过输入缓冲区残留换行符或无效字符用getchar()清空缓冲区或检查格式占位符是否匹配指针程序段错误Segmentation fault指针未初始化、野指针或越界初始化指针为NULL解引用前检查用调试器定位行号Permission denied (publickey)SSH公钥未正确配置执行ssh -T gitgitee.com测试检查公钥是否已粘贴到平台提交后远程文件是中文乱码文件编码和终端代码页不一致VS Code设置文件为UTF-8Windows终端执行chcp 65001这张表只是入门级问题。如果你遇到表格外的报错不要慌把英文报错信息复制到搜索引擎里通常第一页就有答案。学会“根据报错关键词检索”是程序员的基本功甚至可以排在你掌握C语言语法之前。有一个小技巧报错信息的最后一行往往是最关键的一行别一开始就盯着大段输出先看最后一行说什么往往能瞬间定位问题。5.2 我踩过的几个坑第一个坑刚用Git时喜欢git add .把编译出的exe、o文件一股脑提交了仓库体积很快就膨胀起来。后来我养成了“在每个C项目里第一时间创建.gitignore”的习惯。不要嫌它麻烦其实只需要写几行却能让你的仓库多年保持干净。如果没有提前设置已经提交的二进制文件需要使用git rm --cached把文件从索引移除后再写进.gitignore麻烦程度远高于开始就写好。所以我现在每次新建项目第一件事永远是先写.gitignore再写代码。第二个坑提交信息写得太随意。我曾经写过update、fix、123这种信息三个月后看git log完全想不起来当时改了什么。现在我要求自己提交信息尽量动词开头、说明清楚改动的动机和内容。比如“修复scanf输入字符后导致死循环的问题”这样回溯问题时能一眼找到关键提交。如果再配合分支命名规范整个仓库的可读性会非常好。版本管理不是给自己找麻烦而是给未来的自己留一条清晰的回溯路线。第三个坑在C语言里用gets读取字符串。记得有一年课设我为了省事在循环里用gets读了几行输入结果缓冲区被超长输入炸穿程序随机崩溃查了一整天才发现是gets的问题。后来我彻底改成fgets并去掉末尾换行符。C语言里“安全输入”这件事一定要从入门开始重视否则等到代码量大了再回头改成本极高。很多C语言漏洞正是由这种不安全的输入函数引发的虽然我们写的是练习程序但习惯要从一开始就用对。第四个坑指针解引用前不检查NULL。自己写练习时总觉得自己的指针一定有效直到某次读文件时因为文件不存在函数返回了空指针而没有判断就直接解引用程序瞬间段错误。之后我形成了习惯凡是从外部函数拿到的指针都要先判断是否为NULL再使用。虽然多写两行但能避免很多无意义的深夜调试。如果你在写代码的时候能够预判“这里可能有空指针”“这里可能数组越界”写出来的代码质量会明显高一个档次。5.3 一些让你走得更远的小建议C语言和Git这两件事说到底是“动手”的学问。看视频、读教程只能帮你建立概念真正让你学会它们的是亲手敲代码、亲手敲命令、亲手给代码打快照、亲手解决冲突。我强烈建议你在学完这篇博文后立刻完成一个小目标创建一个名为my-c-practice的Git仓库用C语言写出自己的字符串逆序程序并提交然后尝试开一个分支加上“逆序去空格”的功能再合并回主分支。这一套做完你对C语言、Git和VS Code工作流的理解会上一个台阶。如果你学有余力还可以用C语言做一个稍大的练习比如“学生成绩管理”或“图书借阅系统”用结构体数组存储数据再用文件保存记录整个过程用Git记录每次功能迭代。你会发现之前觉得抽象的结构体、指针、文件操作在真实项目里会快速清晰。不要怕慢所有熟练都是从笨拙开始的我自己也不例外。